Here test the strength of recovery of edges of the networks on covariate inflated data passed to LIMON and passed to SPIEC-EASI. These will be compared to non-covariate inflated data passed to SPIEC-EASI. We utilize the LIMON SPIEC-EASI functions to iteratively generate the networks for covariate data passed to SPIEC-EASI, but bypass the linear mixed effect model distribution fitting step of LIMON. This is repeated 5 times for increase subject sample size of 10, 20, 50, 75, and 100.

1. Load Library


Packages required to run the script

library(tidyverse)
library(igraph)
library(NBZIMM)
library(SpiecEasi)
library(LIMON)
library(here)
library(lme4)
library(Matrix)
library(tscount)
library(patchwork)
library(MASS)
library(matrixcalc)
library(gridExtra)
library(devtools)
library(miaSim)
library(reshape2)
library(ggpubr)
library(broom)
library(ggnewscale)
library(coin)

1.2 - Functions

This function will be used to define which edges are consistent across different networks

edge_recovery <- function(data) {
  data <- data %>%
    mutate(
      Recovery = case_when(
        !is.na(L_Edge_weight) & !is.na(O_Edge_weight) & !is.na(Cov_Edge_weight) &
          (L_Edge_weight != 0 & O_Edge_weight != 0 & Cov_Edge_weight != 0) ~ 3,
        !is.na(O_Edge_weight) & !is.na(Cov_Edge_weight) &
          (O_Edge_weight != 0 & Cov_Edge_weight != 0) ~ 2,
        !is.na(L_Edge_weight) & !is.na(O_Edge_weight) &
          (L_Edge_weight != 0 & O_Edge_weight != 0) ~ 1,
        TRUE ~ 0
      )
    )
  
  data
}

2. Model Testing


2.1 - Subjects N=10

2.1.1 - LIMON on Covariate Inflated Data

Run the LIMON covariate correction and network inference steps on Covariate inflated data. These edges will be required as L_Edges

Make LIMON Object

SimData <- read.csv(here("Data", "GLV_SimData", "Dataset_2","GLV_Cov_N10.csv"))

SimData <- column_to_rownames(SimData, "X")

# metadata
meta_data <- SimData[,1:5]
meta_data$Time <- as.numeric(meta_data$Time)
meta_data$BMI <- as.numeric(meta_data$BMI)
meta_data$Age <- as.numeric(meta_data$Age)

# Option to shorten to run faster
limon_meta <- meta_data #%>% filter(Time %in% c(1,2,3,4,5))

# Count data
limon_counts <- as.matrix(SimData[,6:55])

# sort rows
limon_counts <- limon_counts[rownames(limon_meta),]

  
L_obj <- LIMON_Obj(Counts = limon_counts, 
                           SampleData = limon_meta)

Distribution Fitting and check

# Set seed
set.seed(12345)
# Fit the distribution/remove covariates
#################################################################################
L_obj2 <- LIMON_DistrFit(Obj = L_obj, 
                           Time = "Time", 
                           Subject = "ID", 
                           Covariates = c("Sex", "BMI", "Age"),
                           model = "Sex",
                           distribution = "LMM")

Networks of LIMON Data

set.seed(12345)
pseed <- list(rep.num=50, seed=10010)
# SPIEC-EASI per time
L_obj3 <- LIMON_NetInf_Time(Obj = L_obj2, 
                                         method = "glasso", 
                                         sel.criterion = "bstars",
                                         lambda.min.ratio = 0.01,
                                         pulsar.select=TRUE, 
                                         pulsar.params=pseed,
                                         nlambda = 200)

  |                                                                                                
  |                                                                                          |   0%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=========                                                                                 |  10%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |==================                                                                        |  20%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |===========================                                                               |  30%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |====================================                                                      |  40%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=============================================                                             |  50%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |======================================================                                    |  60%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |===============================================================                           |  70%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |========================================================================                  |  80%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=================================================================================         |  90%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |==========================================================================================| 100%
# Print Networks
L_obj4 <- LIMON_Edges_Networks(L_obj3, threshold = 0.0002, vertex.size = 3, 
                                       vertex.label.cex = 8, 
                                       vertex.label.color = "black")

2.1.2 - True Networks - no Covariates

SPIEC-EASI Networks of the Raw Data without covariates. These will be labeled as O_Edges (for original edges). We read in the data and then assign it to a downstream LIMOn object to run SPIEC-EASI across all the timepoints but bypassing the network inference step

# Make fake LIMON Object to Pass to Network Inference Steps
##################################################################################
original_data <- read.csv(here("Data", "GLV_SimData", "Dataset_2","GLV_N10.csv"))
original_data <- original_data %>% column_to_rownames("X")

# Loop through the time points, filter data, and remove metadata
counts_list <- list()
for (i in 1:10) {
  filtered_counts <- original_data %>% filter(Time == i)
  counts_list[[i]] <- round(filtered_counts[, 6:55])
}

# Initialize L_obj_sim2
L_obj_sim2 <- L_obj2

# Assign the processed data to the corresponding slots in L_obj_sim2
for (i in 1:10) {
  L_obj_sim2[["Corrected_Counts_Time"]][[paste0("Corrected_Counts_", i)]] <- counts_list[[i]]
}


# Network Inference
##################################################################################
# Set seed
set.seed(12345)
pseed <- list(rep.num=50, seed=10010)
# SPIEC-EASI per time
L_obj_sim3 <- LIMON_NetInf_Time(Obj = L_obj_sim2, 
                                         method = "glasso", 
                                         sel.criterion = "bstars",
                                         lambda.min.ratio = 0.01,
                                         pulsar.select=TRUE, 
                                         pulsar.params=pseed,
                                         nlambda = 200)

  |                                                                                                
  |                                                                                          |   0%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=========                                                                                 |  10%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |==================                                                                        |  20%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |===========================                                                               |  30%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |====================================                                                      |  40%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=============================================                                             |  50%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |======================================================                                    |  60%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |===============================================================                           |  70%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |========================================================================                  |  80%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=================================================================================         |  90%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |==========================================================================================| 100%
# Print Networks
L_obj_sim4 <- LIMON_Edges_Networks(L_obj_sim3, threshold = 0.0002, vertex.size = 3, 
                                       vertex.label.cex = 8, vertex.label.color = "black")

NA

2.1.3 - SPIEC-EASI on Covariate Data

Similar to process for original networks, except now we will run it on covariate conflated data. These will be labeled as Cov_Edges

# Make fake LIMON Object to Pass to Network Inference Steps
##################################################################################
original_data <- read.csv(here("Data", "GLV_SimData", "Dataset_2","GLV_Cov_N10.csv"))
original_data <- original_data %>% column_to_rownames("X")

# Loop through the time points, filter data, and remove metadata
counts_list <- list()
for (i in 1:10) {
  filtered_counts <- original_data %>% filter(Time == i)
  counts_list[[i]] <- round(filtered_counts[, 6:55])
}

# Initialize L_obj_sim2
L_obj_sim2 <- L_obj2

# Assign the processed data to the corresponding slots in L_obj_sim2
for (i in 1:10) {
  L_obj_sim2[["Corrected_Counts_Time"]][[paste0("Corrected_Counts_", i)]] <- counts_list[[i]]
}



# Network Inference
##################################################################################
# Set seed
set.seed(12345)
pseed <- list(rep.num=50, seed=10010)
# SPIEC-EASI per time
L_obj_sim3 <- LIMON_NetInf_Time(Obj = L_obj_sim2, 
                                         method = "glasso", 
                                         sel.criterion = "bstars",
                                         lambda.min.ratio = 0.01,
                                         pulsar.select=TRUE, 
                                         pulsar.params=pseed,
                                         nlambda = 200)

  |                                                                                                
  |                                                                                          |   0%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=========                                                                                 |  10%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |==================                                                                        |  20%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |===========================                                                               |  30%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |====================================                                                      |  40%
Applying data transformations...
Selecting model with pulsar using bstars...
Warning: Optimal lambda may be outside the supplied valuesFitting final estimate with glasso...
done

  |                                                                                                
  |=============================================                                             |  50%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |======================================================                                    |  60%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |===============================================================                           |  70%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |========================================================================                  |  80%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=================================================================================         |  90%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |==========================================================================================| 100%
# Print Networks
L_obj_sim5 <- LIMON_Edges_Networks(L_obj_sim3, threshold = 0.0002, vertex.size = 3, 
                                       vertex.label.cex = 8, vertex.label.color = "black")

NA
NA
NA

2.1.4 - Merge Outcomes

Calculate the similar edges across the three data types

# LIMON Data - Extract Edges
################################################################################

# Initialize an empty list to store the data frames
L_Edges_List <- list()

# Loop through the edge tables and process each one
for (i in 1:10) {
  edge_table <- as.data.frame(L_obj4[["Edge_Table"]][[paste0("Edge_Table_", i)]])
  # Check if the edge table is empty
  if (nrow(edge_table) == 0) {
    # Create a data frame with NA values
    edge_table <- data.frame(Edge_weight = NA, Time = i)
  } else {
    # dplyr::rename the column and add the Time column
    edge_table <- edge_table %>% dplyr::rename("L_Edge_weight"="Edge_weight")
    edge_table$Time <- i
  }
  # Append the processed table to the list
  L_Edges_List[[i]] <- edge_table
}
# Combine all the processed tables into a single data frame
L_Edges <- bind_rows(L_Edges_List)

# Original Data + Cov - Extract edges
################################################################################

# Initialize an empty list to store the data frames
Cov_Edges_List <- list()

# Loop through the edge tables and process each one
for (i in 1:10) {
  edge_table <- as.data.frame(L_obj_sim5[["Edge_Table"]][[paste0("Edge_Table_", i)]])
  # Check if the edge table is empty
  if (nrow(edge_table) == 0) {
    # Create a data frame with NA values
    edge_table <- data.frame(Edge_weight = NA, Time = i)
  } else {
    # dplyr::rename the column and add the Time column
    edge_table <- edge_table %>% dplyr::rename("Cov_Edge_weight"="Edge_weight")
    edge_table$Time <- i
  }
  # Append the processed table to the list
  Cov_Edges_List[[i]] <- edge_table
}
# Combine all the processed tables into a single data frame
Cov_Edges <- bind_rows(Cov_Edges_List)

# Original Data - Extract Edges
################################################################################
# Initialize an empty list to store the data frames
O_Edges_List <- list()

# Loop through the edge tables and process each one
for (i in 1:10) {
  edge_table <- as.data.frame(L_obj_sim4[["Edge_Table"]][[paste0("Edge_Table_", i)]])
  # Check if the edge table is empty
  if (nrow(edge_table) == 0) {
    # Create a data frame with NA values
    edge_table <- data.frame(Edge_weight = NA, Time = i)
  } else {
    # dplyr::rename the column and add the Time column
    edge_table <- edge_table %>% dplyr::rename("O_Edge_weight"="Edge_weight")
    edge_table$Time <- i
  }
  # Append the processed table to the list
  O_Edges_List[[i]] <- edge_table
}
# Combine all the processed tables into a single data frame
O_Edges <- bind_rows(O_Edges_List)

# Bind data together
################################################################################
Merged_T1 <- merge(L_Edges, O_Edges, by.y = c("Source", "Sink", "Time"), all=TRUE)
Merged_T1 <- merge(Merged_T1, Cov_Edges, by.y = c("Source", "Sink", "Time"), all=TRUE)


# Comparison Graph Data
################################################################################

# Identify true edges
Merged_T10 <- Merged_T1 %>%
  edge_recovery() 

# Add SampleSize 
Merged_T10$SampleSize <- 10

2.2 - Subjects N=20

2.2.1 - LIMON on Covariate Inflated Data

Run the LIMON covariate correction and network inference steps on Covariate inflated data. These edges will be required as L_Edges

Make LIMON Object

SimData <- read.csv(here("Data", "GLV_SimData", "Dataset_2","GLV_Cov_N20.csv"))
SimData <- column_to_rownames(SimData, "X")

# metadata
meta_data <- SimData[,1:5]
meta_data$Time <- as.numeric(meta_data$Time)
meta_data$BMI <- as.numeric(meta_data$BMI)
meta_data$Age <- as.numeric(meta_data$Age)

# Option to shorten to run faster
limon_meta <- meta_data #%>% filter(Time %in% c(1,2,3,4,5))

# Count data
limon_counts <- as.matrix(SimData[,6:55])

# sort rows
limon_counts <- limon_counts[rownames(limon_meta),]

  
L_obj <- LIMON_Obj(Counts = limon_counts, 
                           SampleData = limon_meta)

Distribution Fitting and check

# Set seed
set.seed(12345)
# Fit the distribution/remove covariates
#################################################################################
L_obj2 <- LIMON_DistrFit(Obj = L_obj, 
                           Time = "Time", 
                           Subject = "ID", 
                           Covariates = c("Sex", "BMI", "Age"),
                           model = "Sex",
                           distribution = "LMM")

Networks of LIMON Data

# Set seed
set.seed(12345)
pseed <- list(rep.num=50, seed=10010)

# SPIEC-EASI per time
L_obj3 <- LIMON_NetInf_Time(Obj = L_obj2, 
                                         method = "glasso", 
                                         sel.criterion = "bstars",
                                         lambda.min.ratio = 0.01,
                                         pulsar.select=TRUE, 
                                         pulsar.params=pseed,
                                         nlambda = 200)

  |                                                                                                
  |                                                                                          |   0%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=========                                                                                 |  10%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |==================                                                                        |  20%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |===========================                                                               |  30%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |====================================                                                      |  40%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=============================================                                             |  50%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |======================================================                                    |  60%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |===============================================================                           |  70%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |========================================================================                  |  80%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=================================================================================         |  90%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |==========================================================================================| 100%
# Print Networks
L_obj4 <- LIMON_Edges_Networks(L_obj3, threshold = 0.0002, vertex.size = 3, 
                                       vertex.label.cex = 8, 
                                       vertex.label.color = "black")

2.2.2 - True Networks - no Covariates

SPIEC-EASI Networks of the Raw Data without covariates. These will be labeled as O_Edges (for original edges). We read in the data and then assign it to a downstream LIMOn object to run SPIEC-EASI across all the timepoints but bypassing the network inference step

# Make fake LIMON Object to Pass to Network Inference Steps
##################################################################################
original_data <- read.csv(here("Data", "GLV_SimData", "Dataset_2","GLV_N20.csv"))
original_data <- original_data %>% column_to_rownames("X")

# Loop through the time points, filter data, and remove metadata
counts_list <- list()
for (i in 1:10) {
  filtered_counts <- original_data %>% filter(Time == i)
  counts_list[[i]] <- round(filtered_counts[, 6:55])
}

# Initialize L_obj_sim2
L_obj_sim2 <- L_obj2

# Assign the processed data to the corresponding slots in L_obj_sim2
for (i in 1:10) {
  L_obj_sim2[["Corrected_Counts_Time"]][[paste0("Corrected_Counts_", i)]] <- counts_list[[i]]
}


# Network Inference
##################################################################################
# Set seed
set.seed(12345)
pseed <- list(rep.num=50, seed=10010)
# SPIEC-EASI per time
L_obj_sim3 <- LIMON_NetInf_Time(Obj = L_obj_sim2, 
                                         method = "glasso", 
                                         sel.criterion = "bstars",
                                         lambda.min.ratio = 0.01,
                                         pulsar.select=TRUE, 
                                         pulsar.params=pseed,
                                         nlambda = 200)

  |                                                                                                
  |                                                                                          |   0%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=========                                                                                 |  10%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |==================                                                                        |  20%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |===========================                                                               |  30%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |====================================                                                      |  40%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=============================================                                             |  50%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |======================================================                                    |  60%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |===============================================================                           |  70%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |========================================================================                  |  80%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=================================================================================         |  90%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |==========================================================================================| 100%
# Print Networks
L_obj_sim4 <- LIMON_Edges_Networks(L_obj_sim3, threshold = 0.0002, vertex.size = 3, 
                                       vertex.label.cex = 8, vertex.label.color = "black")

NA
NA
NA

2.2.3 - SPIEC-EASI on Covariate Data

Similar to process for original networks, except now we will run it on covariate conflated data. These will be labeled as Cov_Edges

# Make fake LIMON Object to Pass to Network Inference Steps
##################################################################################
original_data <- read.csv(here("Data", "GLV_SimData", "Dataset_2","GLV_Cov_N20.csv"))
original_data <- original_data %>% column_to_rownames("X")

# Loop through the time points, filter data, and remove metadata
counts_list <- list()
for (i in 1:10) {
  filtered_counts <- original_data %>% filter(Time == i)
  counts_list[[i]] <- round(filtered_counts[, 6:55])
}

# Initialize L_obj_sim2
L_obj_sim2 <- L_obj2

# Assign the processed data to the corresponding slots in L_obj_sim2
for (i in 1:10) {
  L_obj_sim2[["Corrected_Counts_Time"]][[paste0("Corrected_Counts_", i)]] <- counts_list[[i]]
}


# Network Inference
##################################################################################
# Set seed
set.seed(12345)
pseed <- list(rep.num=50, seed=10010)
# SPIEC-EASI per time
L_obj_sim3 <- LIMON_NetInf_Time(Obj = L_obj_sim2, 
                                         method = "glasso", 
                                         sel.criterion = "bstars",
                                         lambda.min.ratio = 0.01,
                                         pulsar.select=TRUE, 
                                         pulsar.params=pseed,
                                         nlambda = 200)

  |                                                                                                
  |                                                                                          |   0%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=========                                                                                 |  10%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |==================                                                                        |  20%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |===========================                                                               |  30%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |====================================                                                      |  40%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=============================================                                             |  50%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |======================================================                                    |  60%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |===============================================================                           |  70%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |========================================================================                  |  80%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=================================================================================         |  90%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |==========================================================================================| 100%
# Print Networks
L_obj_sim5 <- LIMON_Edges_Networks(L_obj_sim3, threshold = 0.0002, vertex.size = 3, 
                                       vertex.label.cex = 8, vertex.label.color = "black")

NA
NA
NA

2.2.4 - Merge Outcomes

Calculate the similar edges across the three data types

# LIMON Data - Extract Edges
################################################################################

# Initialize an empty list to store the data frames
L_Edges_List <- list()

# Loop through the edge tables and process each one
for (i in 1:10) {
  edge_table <- as.data.frame(L_obj4[["Edge_Table"]][[paste0("Edge_Table_", i)]])
  # Check if the edge table is empty
  if (nrow(edge_table) == 0) {
    # Create a data frame with NA values
    edge_table <- data.frame(Edge_weight = NA, Time = i)
  } else {
    # dplyr::rename the column and add the Time column
    edge_table <- edge_table %>% dplyr::rename("L_Edge_weight" = "Edge_weight")
    edge_table$Time <- i
  }
  # Append the processed table to the list
  L_Edges_List[[i]] <- edge_table
}
# Combine all the processed tables into a single data frame
L_Edges <- bind_rows(L_Edges_List)


# Original Data + Cov - Extract edges
################################################################################

# Initialize an empty list to store the data frames
Cov_Edges_List <- list()

# Loop through the edge tables and process each one
for (i in 1:10) {
  edge_table <- as.data.frame(L_obj_sim5[["Edge_Table"]][[paste0("Edge_Table_", i)]])
  # Check if the edge table is empty
  if (nrow(edge_table) == 0) {
    # Create a data frame with NA values
    edge_table <- data.frame(Edge_weight = NA, Time = i)
  } else {
    # dplyr::rename the column and add the Time column
    edge_table <- edge_table %>% dplyr::rename("Cov_Edge_weight"= "Edge_weight")
    edge_table$Time <- i
  }
  # Append the processed table to the list
  Cov_Edges_List[[i]] <- edge_table
}
# Combine all the processed tables into a single data frame
Cov_Edges <- bind_rows(Cov_Edges_List)

# Original Data - Extract Edges
################################################################################
# Initialize an empty list to store the data frames
O_Edges_List <- list()

# Loop through the edge tables and process each one
for (i in 1:10) {
  edge_table <- as.data.frame(L_obj_sim4[["Edge_Table"]][[paste0("Edge_Table_", i)]])
  # Check if the edge table is empty
  if (nrow(edge_table) == 0) {
    # Create a data frame with NA values
    edge_table <- data.frame(Edge_weight = NA, Time = i)
  } else {
    # dplyr::rename the column and add the Time column
    edge_table <- edge_table %>% dplyr::rename("O_Edge_weight"= "Edge_weight")
    edge_table$Time <- i
  }
  # Append the processed table to the list
  O_Edges_List[[i]] <- edge_table
}
# Combine all the processed tables into a single data frame
O_Edges <- bind_rows(O_Edges_List)


# Bind data together
################################################################################
Merged_T1 <- merge(L_Edges, O_Edges, by.y = c("Source", "Sink", "Time"), all=TRUE)
Merged_T1 <- merge(Merged_T1, Cov_Edges, by.y = c("Source", "Sink", "Time"), all=TRUE)


# Comparison Graph Data
################################################################################

# Identify true edges
Merged_T20 <- Merged_T1 %>%
  edge_recovery() 

# Add SampleSize 
Merged_T20$SampleSize <- 20

2.3 - Subjects N=50

2.3.1 - LIMON on Covariate Inflated Data

Run the LIMON covariate correction and network inference steps on Covariate inflated data. These edges will be required as L_Edges

Make LIMON Object

SimData <- read.csv(here("Data", "GLV_SimData", "Dataset_2","GLV_Cov_N50.csv"))
SimData <- column_to_rownames(SimData, "X")

# metadata
meta_data <- SimData[,1:5]
meta_data$Time <- as.numeric(meta_data$Time)
meta_data$BMI <- as.numeric(meta_data$BMI)
meta_data$Age <- as.numeric(meta_data$Age)

# Option to shorten to run faster
limon_meta <- meta_data #%>% filter(Time %in% c(1,2,3,4,5))

# Count data
limon_counts <- as.matrix(SimData[,6:55])

# sort rows
limon_counts <- limon_counts[rownames(limon_meta),]

  
L_obj <- LIMON_Obj(Counts = limon_counts, 
                           SampleData = limon_meta)

Distribution Fitting and check

# Set seed
set.seed(12345)
# Fit the distribution/remove covariates
#################################################################################
L_obj2 <- LIMON_DistrFit(Obj = L_obj, 
                           Time = "Time", 
                           Subject = "ID", 
                           Covariates = c("Sex", "BMI", "Age"),
                           model = "Sex",
                           distribution = "LMM")

Networks of LIMON Data

# Set seed
set.seed(12345)
pseed <- list(rep.num=50, seed=10010)

# SPIEC-EASI per time
L_obj3 <- LIMON_NetInf_Time(Obj = L_obj2, 
                                         method = "glasso", 
                                         sel.criterion = "bstars",
                                         lambda.min.ratio = 0.01,
                                         pulsar.select=TRUE, 
                                         pulsar.params=pseed,
                                         nlambda = 200)

  |                                                                                                
  |                                                                                          |   0%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=========                                                                                 |  10%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |==================                                                                        |  20%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |===========================                                                               |  30%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |====================================                                                      |  40%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=============================================                                             |  50%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |======================================================                                    |  60%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |===============================================================                           |  70%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |========================================================================                  |  80%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=================================================================================         |  90%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |==========================================================================================| 100%
# Print Networks
L_obj4 <- LIMON_Edges_Networks(L_obj3, threshold = 0.0002, vertex.size = 3, 
                                       vertex.label.cex = 8, 
                                       vertex.label.color = "black")

2.3.2 - True Networks - no Covariates

SPIEC-EASI Networks of the Raw Data without covariates. These will be labeled as O_Edges (for original edges). We read in the data and then assign it to a downstream LIMOn object to run SPIEC-EASI across all the time points but bypassing the network inference step

# Make fake LIMON Object to Pass to Network Inference Steps
##################################################################################
original_data <- read.csv(here("Data", "GLV_SimData", "Dataset_2","GLV_N50.csv"))
original_data <- original_data %>% column_to_rownames("X")

# Loop through the time points, filter data, and remove metadata
counts_list <- list()
for (i in 1:10) {
  filtered_counts <- original_data %>% filter(Time == i)
  counts_list[[i]] <- round(filtered_counts[, 6:55])
}

# Initialize L_obj_sim2
L_obj_sim2 <- L_obj2

# Assign the processed data to the corresponding slots in L_obj_sim2
for (i in 1:10) {
  L_obj_sim2[["Corrected_Counts_Time"]][[paste0("Corrected_Counts_", i)]] <- counts_list[[i]]
}


# Network Inference
##################################################################################
# Set seed
set.seed(12345)
pseed <- list(rep.num=50, seed=10010)
# SPIEC-EASI per time
L_obj_sim3 <- LIMON_NetInf_Time(Obj = L_obj_sim2, 
                                         method = "glasso", 
                                         sel.criterion = "bstars",
                                         lambda.min.ratio = 0.01,
                                         pulsar.select=TRUE, 
                                         pulsar.params=pseed,
                                         nlambda = 200)

  |                                                                                                
  |                                                                                          |   0%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=========                                                                                 |  10%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |==================                                                                        |  20%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |===========================                                                               |  30%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |====================================                                                      |  40%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=============================================                                             |  50%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |======================================================                                    |  60%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |===============================================================                           |  70%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |========================================================================                  |  80%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=================================================================================         |  90%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |==========================================================================================| 100%
# Print Networks
L_obj_sim4 <- LIMON_Edges_Networks(L_obj_sim3, threshold = 0.0002, vertex.size = 3, 
                                       vertex.label.cex = 8, vertex.label.color = "black")

NA
NA
NA

2.3.3 - SPIEC-EASI on Covariate Data

Similar to process for original networks, except now we will run it on covariate conflated data. These will be labeled as Cov_Edges

# Make fake LIMON Object to Pass to Network Inference Steps
##################################################################################
original_data <- read.csv(here("Data", "GLV_SimData", "Dataset_2","GLV_Cov_N50.csv"))
original_data <- original_data %>% column_to_rownames("X")

# Loop through the time points, filter data, and remove metadata
counts_list <- list()
for (i in 1:10) {
  filtered_counts <- original_data %>% filter(Time == i)
  counts_list[[i]] <- round(filtered_counts[, 6:55])
}

# Initialize L_obj_sim2
L_obj_sim2 <- L_obj2

# Assign the processed data to the corresponding slots in L_obj_sim2
for (i in 1:10) {
  L_obj_sim2[["Corrected_Counts_Time"]][[paste0("Corrected_Counts_", i)]] <- counts_list[[i]]
}


# Network Inference
##################################################################################
# Set seed
set.seed(12345)
pseed <- list(rep.num=50, seed=10010)
# SPIEC-EASI per time
L_obj_sim3 <- LIMON_NetInf_Time(Obj = L_obj_sim2, 
                                         method = "glasso", 
                                         sel.criterion = "bstars",
                                         lambda.min.ratio = 0.01,
                                         pulsar.select=TRUE, 
                                         pulsar.params=pseed,
                                         nlambda = 200)

  |                                                                                                
  |                                                                                          |   0%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=========                                                                                 |  10%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |==================                                                                        |  20%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |===========================                                                               |  30%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |====================================                                                      |  40%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=============================================                                             |  50%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |======================================================                                    |  60%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |===============================================================                           |  70%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |========================================================================                  |  80%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=================================================================================         |  90%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |==========================================================================================| 100%
# Print Networks
L_obj_sim5 <- LIMON_Edges_Networks(L_obj_sim3, threshold = 0.0002, vertex.size = 3, 
                                       vertex.label.cex = 8, vertex.label.color = "black")

NA
NA
NA

2.3.4 - Merge Outcomes

Calculate the similar edges across the three data types

# LIMON Data - Extract Edges
################################################################################

# Initialize an empty list to store the data frames
L_Edges_List <- list()

# Loop through the edge tables and process each one
for (i in 1:10) {
  edge_table <- as.data.frame(L_obj4[["Edge_Table"]][[paste0("Edge_Table_", i)]])
  # Check if the edge table is empty
  if (nrow(edge_table) == 0) {
    # Create a data frame with NA values
    edge_table <- data.frame(Edge_weight = NA, Time = i)
  } else {
    # dplyr::rename the column and add the Time column
    edge_table <- edge_table %>% dplyr::rename("L_Edge_weight"= "Edge_weight")
    edge_table$Time <- i
  }
  # Append the processed table to the list
  L_Edges_List[[i]] <- edge_table
}
# Combine all the processed tables into a single data frame
L_Edges <- bind_rows(L_Edges_List)


# Original Data + Cov - Extract edges
################################################################################

# Initialize an empty list to store the data frames
Cov_Edges_List <- list()

# Loop through the edge tables and process each one
for (i in 1:10) {
  edge_table <- as.data.frame(L_obj_sim5[["Edge_Table"]][[paste0("Edge_Table_", i)]])
  # Check if the edge table is empty
  if (nrow(edge_table) == 0) {
    # Create a data frame with NA values
    edge_table <- data.frame(Edge_weight = NA, Time = i)
  } else {
    # dplyr::rename the column and add the Time column
    edge_table <- edge_table %>% dplyr::rename("Cov_Edge_weight"= "Edge_weight")
    edge_table$Time <- i
  }
  # Append the processed table to the list
  Cov_Edges_List[[i]] <- edge_table
}
# Combine all the processed tables into a single data frame
Cov_Edges <- bind_rows(Cov_Edges_List)

# Original Data - Extract Edges
################################################################################
# Initialize an empty list to store the data frames
O_Edges_List <- list()

# Loop through the edge tables and process each one
for (i in 1:10) {
  edge_table <- as.data.frame(L_obj_sim4[["Edge_Table"]][[paste0("Edge_Table_", i)]])
  # Check if the edge table is empty
  if (nrow(edge_table) == 0) {
    # Create a data frame with NA values
    edge_table <- data.frame(Edge_weight = NA, Time = i)
  } else {
    # dplyr::rename the column and add the Time column
    edge_table <- edge_table %>% dplyr::rename("O_Edge_weight"= "Edge_weight")
    edge_table$Time <- i
  }
  # Append the processed table to the list
  O_Edges_List[[i]] <- edge_table
}
# Combine all the processed tables into a single data frame
O_Edges <- bind_rows(O_Edges_List)

# Bind data together
################################################################################
Merged_T1 <- merge(L_Edges, O_Edges, by.y = c("Source", "Sink", "Time"), all=TRUE)
Merged_T1 <- merge(Merged_T1, Cov_Edges, by.y = c("Source", "Sink", "Time"), all=TRUE)


# Comparison Graph Data
################################################################################

# Identify true edges
Merged_T50 <- Merged_T1 %>%
  edge_recovery() 

# Add SampleSize 
Merged_T50$SampleSize <- 50

2.4 - Subjects N=75

2.4.1 - LIMON on Covariate Inflated Data

Run the LIMON covariate correction and network inference steps on Covariate inflated data. These edges will be required as L_Edges

Make LIMON Object

SimData <- read.csv(here("Data", "GLV_SimData", "Dataset_2","GLV_Cov_N75.csv"))
SimData <- column_to_rownames(SimData, "X")

# metadata
meta_data <- SimData[,1:5]
meta_data$Time <- as.numeric(meta_data$Time)
meta_data$BMI <- as.numeric(meta_data$BMI)
meta_data$Age <- as.numeric(meta_data$Age)

# Option to shorten to run faster
limon_meta <- meta_data #%>% filter(Time %in% c(1,2,3,4,5))

# Count data
limon_counts <- as.matrix(SimData[,6:55])

# sort rows
limon_counts <- limon_counts[rownames(limon_meta),]

  
L_obj <- LIMON_Obj(Counts = limon_counts, 
                           SampleData = limon_meta)

Distribution Fitting and check

# Set seed
set.seed(12345)
# Fit the distribution/remove covariates
#################################################################################
L_obj2 <- LIMON_DistrFit(Obj = L_obj, 
                           Time = "Time", 
                           Subject = "ID", 
                           Covariates = c("Sex", "BMI", "Age"),
                           model = "Sex",
                           distribution = "LMM")

Networks of LIMON Data

# Set seed
set.seed(12345)
pseed <- list(rep.num=50, seed=10010)

# SPIEC-EASI per time
L_obj3 <- LIMON_NetInf_Time(Obj = L_obj2, 
                                         method = "glasso", 
                                         sel.criterion = "bstars",
                                         lambda.min.ratio = 0.01,
                                         pulsar.select=TRUE, 
                                         pulsar.params=pseed,
                                         nlambda = 200)

  |                                                                                                
  |                                                                                          |   0%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=========                                                                                 |  10%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |==================                                                                        |  20%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |===========================                                                               |  30%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |====================================                                                      |  40%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=============================================                                             |  50%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |======================================================                                    |  60%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |===============================================================                           |  70%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |========================================================================                  |  80%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=================================================================================         |  90%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |==========================================================================================| 100%
# Print Networks
L_obj4 <- LIMON_Edges_Networks(L_obj3, threshold = 0.0002, vertex.size = 3, 
                                       vertex.label.cex = 8, 
                                       vertex.label.color = "black")

2.4.2 - True Networks - no Covariates

SPIEC-EASI Networks of the Raw Data without covariates. These will be labeled as O_Edges (for original edges). We read in the data and then assign it to a downstream LIMOn object to run SPIEC-EASI across all the timepoints but bypassing the network inference step

# Make fake LIMON Object to Pass to Network Inference Steps
##################################################################################
original_data <- read.csv(here("Data", "GLV_SimData", "Dataset_2","GLV_N75.csv"))
original_data <- original_data %>% column_to_rownames("X")

# Loop through the time points, filter data, and remove metadata
counts_list <- list()
for (i in 1:10) {
  filtered_counts <- original_data %>% filter(Time == i)
  counts_list[[i]] <- round(filtered_counts[, 6:55])
}

# Initialize L_obj_sim2
L_obj_sim2 <- L_obj2

# Assign the processed data to the corresponding slots in L_obj_sim2
for (i in 1:10) {
  L_obj_sim2[["Corrected_Counts_Time"]][[paste0("Corrected_Counts_", i)]] <- counts_list[[i]]
}


# Network Inference
##################################################################################
# Set seed
set.seed(12345)
pseed <- list(rep.num=50, seed=10010)
# SPIEC-EASI per time
L_obj_sim3 <- LIMON_NetInf_Time(Obj = L_obj_sim2, 
                                         method = "glasso", 
                                         sel.criterion = "bstars",
                                         lambda.min.ratio = 0.01,
                                         pulsar.select=TRUE, 
                                         pulsar.params=pseed,
                                         nlambda = 200)

  |                                                                                                
  |                                                                                          |   0%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=========                                                                                 |  10%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |==================                                                                        |  20%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |===========================                                                               |  30%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |====================================                                                      |  40%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=============================================                                             |  50%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |======================================================                                    |  60%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |===============================================================                           |  70%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |========================================================================                  |  80%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=================================================================================         |  90%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |==========================================================================================| 100%
# Print Networks
L_obj_sim4 <- LIMON_Edges_Networks(L_obj_sim3, threshold = 0.0002, vertex.size = 3, 
                                       vertex.label.cex = 8, vertex.label.color = "black")

NA
NA
NA

2.4.3 - SPIEC-EASI on Covariate Data

Similar to process for original networks, except now we will run it on covariate conflated data. These will be labeled as Cov_Edges

# Make fake LIMON Object to Pass to Network Inference Steps
##################################################################################
original_data <- read.csv(here("Data", "GLV_SimData", "Dataset_2","GLV_Cov_N75.csv"))
original_data <- original_data %>% column_to_rownames("X")

# Loop through the time points, filter data, and remove metadata
counts_list <- list()
for (i in 1:10) {
  filtered_counts <- original_data %>% filter(Time == i)
  counts_list[[i]] <- round(filtered_counts[, 6:55])
}

# Initialize L_obj_sim2
L_obj_sim2 <- L_obj2

# Assign the processed data to the corresponding slots in L_obj_sim2
for (i in 1:10) {
  L_obj_sim2[["Corrected_Counts_Time"]][[paste0("Corrected_Counts_", i)]] <- counts_list[[i]]
}


# Network Inference
##################################################################################
# Set seed
set.seed(12345)
pseed <- list(rep.num=50, seed=10010)
# SPIEC-EASI per time
L_obj_sim3 <- LIMON_NetInf_Time(Obj = L_obj_sim2, 
                                         method = "glasso", 
                                         sel.criterion = "bstars",
                                         lambda.min.ratio = 0.01,
                                         pulsar.select=TRUE, 
                                         pulsar.params=pseed,
                                         nlambda = 200)

  |                                                                                                
  |                                                                                          |   0%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=========                                                                                 |  10%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |==================                                                                        |  20%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |===========================                                                               |  30%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |====================================                                                      |  40%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=============================================                                             |  50%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |======================================================                                    |  60%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |===============================================================                           |  70%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |========================================================================                  |  80%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=================================================================================         |  90%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |==========================================================================================| 100%
# Print Networks
L_obj_sim5 <- LIMON_Edges_Networks(L_obj_sim3, threshold = 0.0002, vertex.size = 3, 
                                       vertex.label.cex = 8, vertex.label.color = "black")

NA
NA
NA

2.4.4 - Merge Outcomes

Calculate the similar edges across the three data types

# LIMON Data - Extract Edges
################################################################################

# Initialize an empty list to store the data frames
L_Edges_List <- list()

# Loop through the edge tables and process each one
for (i in 1:10) {
  edge_table <- as.data.frame(L_obj4[["Edge_Table"]][[paste0("Edge_Table_", i)]])
  # Check if the edge table is empty
  if (nrow(edge_table) == 0) {
    # Create a data frame with NA values
    edge_table <- data.frame(Edge_weight = NA, Time = i)
  } else {
    # dplyr::rename the column and add the Time column
    edge_table <- edge_table %>% dplyr::rename("L_Edge_weight"= "Edge_weight")
    edge_table$Time <- i
  }
  # Append the processed table to the list
  L_Edges_List[[i]] <- edge_table
}
# Combine all the processed tables into a single data frame
L_Edges <- bind_rows(L_Edges_List)

# Original Data + Cov - Extract edges
################################################################################

# Initialize an empty list to store the data frames
Cov_Edges_List <- list()

# Loop through the edge tables and process each one
for (i in 1:10) {
  edge_table <- as.data.frame(L_obj_sim5[["Edge_Table"]][[paste0("Edge_Table_", i)]])
  # Check if the edge table is empty
  if (nrow(edge_table) == 0) {
    # Create a data frame with NA values
    edge_table <- data.frame(Edge_weight = NA, Time = i)
  } else {
    # dplyr::rename the column and add the Time column
    edge_table <- edge_table %>% dplyr::rename("Cov_Edge_weight"= "Edge_weight")
    edge_table$Time <- i
  }
  # Append the processed table to the list
  Cov_Edges_List[[i]] <- edge_table
}
# Combine all the processed tables into a single data frame
Cov_Edges <- bind_rows(Cov_Edges_List)

# Original Data - Extract Edges
################################################################################
# Initialize an empty list to store the data frames
O_Edges_List <- list()

# Loop through the edge tables and process each one
for (i in 1:10) {
  edge_table <- as.data.frame(L_obj_sim4[["Edge_Table"]][[paste0("Edge_Table_", i)]])
  # Check if the edge table is empty
  if (nrow(edge_table) == 0) {
    # Create a data frame with NA values
    edge_table <- data.frame(Edge_weight = NA, Time = i)
  } else {
    # dplyr::rename the column and add the Time column
    edge_table <- edge_table %>% dplyr::rename("O_Edge_weight"= "Edge_weight")
    edge_table$Time <- i
  }
  # Append the processed table to the list
  O_Edges_List[[i]] <- edge_table
}
# Combine all the processed tables into a single data frame
O_Edges <- bind_rows(O_Edges_List)

# Bind data together
################################################################################
Merged_T1 <- merge(L_Edges, O_Edges, by.y = c("Source", "Sink", "Time"), all=TRUE)
Merged_T1 <- merge(Merged_T1, Cov_Edges, by.y = c("Source", "Sink", "Time"), all=TRUE)


# Comparison Graph Data
################################################################################

# Identify true edges
Merged_T75 <- Merged_T1 %>%
  edge_recovery() 

# Add SampleSize 
Merged_T75$SampleSize <- 75

2.5 - Subjects N=100

2.5.1 - LIMON on Covariate Inflated Data

Run the LIMON covariate correction and network inference steps on Covariate inflated data. These edges will be required as L_Edges

Make LIMON Object

SimData <- read.csv(here("Data", "GLV_SimData", "Dataset_2","GLV_Cov_N100.csv"))
SimData <- column_to_rownames(SimData, "X")

# metadata
meta_data <- SimData[,1:5]
meta_data$Time <- as.numeric(meta_data$Time)
meta_data$BMI <- as.numeric(meta_data$BMI)
meta_data$Age <- as.numeric(meta_data$Age)

# Option to shorten to run faster
limon_meta <- meta_data #%>% filter(Time %in% c(1,2,3,4,5))

# Count data
limon_counts <- as.matrix(SimData[,6:55])

# sort rows
limon_counts <- limon_counts[rownames(limon_meta),]

  
L_obj <- LIMON_Obj(Counts = limon_counts, 
                           SampleData = limon_meta)

Distribution Fitting and check

# Set seed
set.seed(12345)
# Fit the distribution/remove covariates
#################################################################################
L_obj2 <- LIMON_DistrFit(Obj = L_obj, 
                           Time = "Time", 
                           Subject = "ID", 
                           Covariates = c("Sex", "BMI", "Age"),
                           model = "Sex",
                           distribution = "LMM")

Networks of LIMON Data

# Set seed
set.seed(12345)
pseed <- list(rep.num=50, seed=10010)

# SPIEC-EASI per time
L_obj3 <- LIMON_NetInf_Time(Obj = L_obj2, 
                                         method = "glasso", 
                                         sel.criterion = "bstars",
                                         lambda.min.ratio = 0.01,
                                         pulsar.select=TRUE, 
                                         pulsar.params=pseed,
                                         nlambda = 200)

  |                                                                                                
  |                                                                                          |   0%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=========                                                                                 |  10%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |==================                                                                        |  20%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |===========================                                                               |  30%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |====================================                                                      |  40%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=============================================                                             |  50%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |======================================================                                    |  60%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |===============================================================                           |  70%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |========================================================================                  |  80%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=================================================================================         |  90%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |==========================================================================================| 100%
# Print Networks
L_obj4 <- LIMON_Edges_Networks(L_obj3, threshold = 0.0002, vertex.size = 3, 
                                       vertex.label.cex = 8, 
                                       vertex.label.color = "black")

2.5.2 - True Networks - no Covariates

SPIEC-EASI Networks of the Raw Data without covariates. These will be labeled as O_Edges (for original edges). We read in the data and then assign it to a downstream LIMOn object to run SPIEC-EASI across all the timepoints but bypassing the network inference step

# Make fake LIMON Object to Pass to Network Inference Steps
##################################################################################
original_data <- read.csv(here("Data", "GLV_SimData", "Dataset_2","GLV_N100.csv"))
original_data <- original_data %>% column_to_rownames("X")

# Loop through the time points, filter data, and remove metadata
counts_list <- list()
for (i in 1:10) {
  filtered_counts <- original_data %>% filter(Time == i)
  counts_list[[i]] <- round(filtered_counts[, 6:55])
}

# Initialize L_obj_sim2
L_obj_sim2 <- L_obj2

# Assign the processed data to the corresponding slots in L_obj_sim2
for (i in 1:10) {
  L_obj_sim2[["Corrected_Counts_Time"]][[paste0("Corrected_Counts_", i)]] <- counts_list[[i]]
}


# Network Inference
##################################################################################
# Set seed
set.seed(12345)
pseed <- list(rep.num=50, seed=10010)
# SPIEC-EASI per time
L_obj_sim3 <- LIMON_NetInf_Time(Obj = L_obj_sim2, 
                                         method = "glasso", 
                                         sel.criterion = "bstars",
                                         lambda.min.ratio = 0.01,
                                         pulsar.select=TRUE, 
                                         pulsar.params=pseed,
                                         nlambda = 200)

  |                                                                                                
  |                                                                                          |   0%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=========                                                                                 |  10%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |==================                                                                        |  20%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |===========================                                                               |  30%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |====================================                                                      |  40%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=============================================                                             |  50%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |======================================================                                    |  60%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |===============================================================                           |  70%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |========================================================================                  |  80%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=================================================================================         |  90%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |==========================================================================================| 100%
# Print Networks
L_obj_sim4 <- LIMON_Edges_Networks(L_obj_sim3, threshold = 0.0002, vertex.size = 3, 
                                       vertex.label.cex = 8, vertex.label.color = "black")

NA
NA
NA

2.5.3 - SPIEC-EASI on Covariate Data

Similar to process for original networks, except now we will run it on covariate conflated data. These will be labeled as Cov_Edges

# Make fake LIMON Object to Pass to Network Inference Steps
##################################################################################
original_data <- read.csv(here("Data", "GLV_SimData", "Dataset_2","GLV_Cov_N100.csv"))
original_data <- original_data %>% column_to_rownames("X")

# Loop through the time points, filter data, and remove metadata
counts_list <- list()
for (i in 1:10) {
  filtered_counts <- original_data %>% filter(Time == i)
  counts_list[[i]] <- round(filtered_counts[, 6:55])
}

# Initialize L_obj_sim2
L_obj_sim2 <- L_obj2

# Assign the processed data to the corresponding slots in L_obj_sim2
for (i in 1:10) {
  L_obj_sim2[["Corrected_Counts_Time"]][[paste0("Corrected_Counts_", i)]] <- counts_list[[i]]
}


# Network Inference
##################################################################################
# Set seed
set.seed(12345)
pseed <- list(rep.num=50, seed=10010)
# SPIEC-EASI per time
L_obj_sim3 <- LIMON_NetInf_Time(Obj = L_obj_sim2, 
                                         method = "glasso", 
                                         sel.criterion = "bstars",
                                         lambda.min.ratio = 0.01,
                                         pulsar.select=TRUE, 
                                         pulsar.params=pseed,
                                         nlambda = 200)

  |                                                                                                
  |                                                                                          |   0%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=========                                                                                 |  10%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |==================                                                                        |  20%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |===========================                                                               |  30%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |====================================                                                      |  40%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=============================================                                             |  50%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |======================================================                                    |  60%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |===============================================================                           |  70%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |========================================================================                  |  80%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |=================================================================================         |  90%
Applying data transformations...
Selecting model with pulsar using bstars...
Fitting final estimate with glasso...
done

  |                                                                                                
  |==========================================================================================| 100%
# Print Networks
L_obj_sim5 <- LIMON_Edges_Networks(L_obj_sim3, threshold = 0.0002, vertex.size = 3, 
                                       vertex.label.cex = 8, vertex.label.color = "black")

NA
NA
NA

2.5.4 - Merge Outcomes

Calculate the similar edges across the three data types

# LIMON Data - Extract Edges
################################################################################

# Initialize an empty list to store the data frames
L_Edges_List <- list()

# Loop through the edge tables and process each one
for (i in 1:10) {
  edge_table <- as.data.frame(L_obj4[["Edge_Table"]][[paste0("Edge_Table_", i)]])
  # Check if the edge table is empty
  if (nrow(edge_table) == 0) {
    # Create a data frame with NA values
    edge_table <- data.frame(Edge_weight = NA, Time = i)
  } else {
    # dplyr::rename the column and add the Time column
    edge_table <- edge_table %>% dplyr::rename("L_Edge_weight"= "Edge_weight")
    edge_table$Time <- i
  }
  # Append the processed table to the list
  L_Edges_List[[i]] <- edge_table
}
# Combine all the processed tables into a single data frame
L_Edges <- bind_rows(L_Edges_List)

# Original Data + Cov - Extract edges
################################################################################

# Initialize an empty list to store the data frames
Cov_Edges_List <- list()

# Loop through the edge tables and process each one
for (i in 1:10) {
  edge_table <- as.data.frame(L_obj_sim5[["Edge_Table"]][[paste0("Edge_Table_", i)]])
  # Check if the edge table is empty
  if (nrow(edge_table) == 0) {
    # Create a data frame with NA values
    edge_table <- data.frame(Edge_weight = NA, Time = i)
  } else {
    # dplyr::rename the column and add the Time column
    edge_table <- edge_table %>% dplyr::rename("Cov_Edge_weight"= "Edge_weight")
    edge_table$Time <- i
  }
  # Append the processed table to the list
  Cov_Edges_List[[i]] <- edge_table
}
# Combine all the processed tables into a single data frame
Cov_Edges <- bind_rows(Cov_Edges_List)

# Original Data - Extract Edges
################################################################################
# Initialize an empty list to store the data frames
O_Edges_List <- list()

# Loop through the edge tables and process each one
for (i in 1:10) {
  edge_table <- as.data.frame(L_obj_sim4[["Edge_Table"]][[paste0("Edge_Table_", i)]])
  # Check if the edge table is empty
  if (nrow(edge_table) == 0) {
    # Create a data frame with NA values
    edge_table <- data.frame(Edge_weight = NA, Time = i)
  } else {
    # dplyr::rename the column and add the Time column
    edge_table <- edge_table %>% dplyr::rename("O_Edge_weight"= "Edge_weight")
    edge_table$Time <- i
  }
  # Append the processed table to the list
  O_Edges_List[[i]] <- edge_table
}
# Combine all the processed tables into a single data frame
O_Edges <- bind_rows(O_Edges_List)

# Bind data together
################################################################################
Merged_T1 <- merge(L_Edges, O_Edges, by.y = c("Source", "Sink", "Time"), all=TRUE)
Merged_T1 <- merge(Merged_T1, Cov_Edges, by.y = c("Source", "Sink", "Time"), all=TRUE)


# Comparison Graph Data
################################################################################

# Identify true edges
Merged_T100 <- Merged_T1 %>%
  edge_recovery() 

# Add SampleSize 
Merged_T100$SampleSize <- 100

3 - Summary of Sensitivty Analysis


Merge data for the following graphical analysis

# Drop Source and Sink column and combine together
#################################################################
Merged_T1b <- Merged_T10 %>% dplyr::select(-Source, -Sink)
Merged_T20b <- Merged_T20 %>% dplyr::select(-Source, -Sink)
Merged_T50b <- Merged_T50 %>% dplyr::select(-Source, -Sink)
Merged_T75b <- Merged_T75 %>% dplyr::select(-Source, -Sink)
Merged_T100b <- Merged_T100 %>% dplyr::select(-Source, -Sink)

# Merge together
#################################################################
# Data for Graphs 1 and 2
Subject_sens_data_full <- rbind(Merged_T1b, Merged_T20b, Merged_T50b, Merged_T75b, Merged_T100b)

# Data for Graph 3
#Merged_T100c <- Merged_T100 %>% dplyr::select(-Edge_weight)
Merged_T10c <- Merged_T10[,c("Source","Sink","Time","L_Edge_weight",
                            "O_Edge_weight","Cov_Edge_weight","Recovery","SampleSize")]
Subject_sens_data_taxa <- rbind(Merged_T10c, Merged_T20, Merged_T50, Merged_T75, Merged_T100)

Option to write and read in the above files

#write.csv(Subject_sens_data_full, here("Output", "Dataset_2", "Subject_sens_data_full.csv"))
#write.csv(Subject_sens_data_taxa, here("Output", "Dataset_2","Subject_sens_data_taxa.csv"))
Subject_sens_data_full <- read.csv(here("Output", "Dataset_2", "Subject_sens_data_full.csv"))
Subject_sens_data_taxa <- read.csv(here("Output","Dataset_2", "Subject_sens_data_taxa.csv"))

3.1 Graph Percent True Edges

Find the Percent True Edges Recovered per sample size per time point

3.1.1 - Data Formatting

# Loop through all timepoints
##########################################################
all_timepoints <- list()

for (time in 1:10) {
  # Filter data for the current Sample Size
  Subject_sens_data <- Subject_sens_data_full %>% filter(Time == time)
  
  # Countthe total True_Edges for that Sample Size
  ##########################################################
  
  Subject_sens_data2 <- Subject_sens_data %>%
    group_by(SampleSize) %>%
    summarise(True_Edges = sum(O_Edge_weight != 0, na.rm = TRUE)) %>%
    ungroup()
  
  # Merge the summarized data back with the original data
  Subject_sens_data <- merge(Subject_sens_data, Subject_sens_data2, by="SampleSize", all=TRUE)
  
  # Find the Percent True Edges Data
  ##########################################################
  
  # count the number of edges
  Percent_true <- Subject_sens_data  %>%
    group_by(SampleSize, Recovery, True_Edges) %>%
    summarise(Count = n()) %>%
    ungroup() %>%
    filter(Recovery != 0)
  
  # Add the number of edges in all three data types to the LIMON vs Raw Speic-Easi counts
  Percent_true <- Percent_true %>%
    group_by(SampleSize, True_Edges) %>%
    mutate(
      Count_3 = ifelse(Recovery == 3, Count, 0),
      Count = ifelse(Recovery == 1 | Recovery == 2, Count + sum(Count_3), Count)
    ) %>%
    dplyr::select(-Count_3)
  
  # Create a dataframe with all combinations of SampleSize and Recovery
  combinations <- expand.grid(SampleSize = unique(Percent_true$SampleSize), Recovery = c(1, 2, 3))
  Percent_true <- merge(Percent_true, combinations, by=c("SampleSize", "Recovery"), all=TRUE)
  
  # Fill and replace missing values
  Percent_true <- Percent_true %>%
    group_by(SampleSize) %>%
    fill(True_Edges, .direction = "downup") %>%
    mutate(
      Count = ifelse(is.na(Count), Count[Recovery == 3][1], ifelse(is.na(Count), 0, Count))
    ) %>%
    ungroup() %>%
    replace_na(list(Count = 0)) %>% 
    mutate(DataType = case_when(
      Recovery == 1 ~ "LIMON",
      Recovery == 2 ~ "SPIEC-EASI",
      Recovery == 3 ~ "Both"
    ))
  
  # Calculate the Percentage of edges
  Percent_true$Percent_true <- Percent_true$Count / Percent_true$True_Edges
  Percent_true$Time <- time
  
  # Store the result in the list
  all_timepoints[[time]] <- Percent_true
}
`summarise()` has grouped output by 'SampleSize', 'Recovery'. You can override using the `.groups` argument.`summarise()` has grouped output by 'SampleSize', 'Recovery'. You can override using the `.groups` argument.`summarise()` has grouped output by 'SampleSize', 'Recovery'. You can override using the `.groups` argument.`summarise()` has grouped output by 'SampleSize', 'Recovery'. You can override using the `.groups` argument.`summarise()` has grouped output by 'SampleSize', 'Recovery'. You can override using the `.groups` argument.`summarise()` has grouped output by 'SampleSize', 'Recovery'. You can override using the `.groups` argument.`summarise()` has grouped output by 'SampleSize', 'Recovery'. You can override using the `.groups` argument.`summarise()` has grouped output by 'SampleSize', 'Recovery'. You can override using the `.groups` argument.`summarise()` has grouped output by 'SampleSize', 'Recovery'. You can override using the `.groups` argument.`summarise()` has grouped output by 'SampleSize', 'Recovery'. You can override using the `.groups` argument.
# Combine all timepoints together
Percent_true <- do.call(rbind, all_timepoints)

# Filter the combined data
Percent_true <- Percent_true %>% filter(DataType != "Both")

3.1.2 - Statistics

Check normalcy to determine if t-test or if wilcox rank sum

# Step 1 - Run Shapiro Wilks to test for normalacy of the data
#############################################################################
shapiro_result <- shapiro.test(Percent_true$Percent_true)
p_value <- shapiro_result$p.value

# Step 2 - Print p-value on the histogram
#############################################################################
# Create the histogram plot
hist(Percent_true$Percent_true, breaks=100, 
     main = "Distribution of Percent True", 
     xlab = "Percent True", ylab = "Frequency")
# Add the p-value annotation
text(x = 0.4, y = 10, 
     labels = paste("Shapiro-Wilk p-value: ", p_value))

Data is not normally distributed so use wilcox rank sum test instead of t-test

p-value label function

# P-value labels
p_value_sig <- function(p) {
  if (is.na(p)) {
    return(" ")
  } else if (p < 0.001) {
    return("***")
  } else if (p < 0.01) {
    return("**")
  } else if (p < 0.05) {
    return("*")
  } else {
    return(" ")
  }
}

Plot the findings


# Step 1: Summary Statistics
#################################################################

# Perform a T-tests and make summary statistics for plotting
Summary_data <- Percent_true %>%
  group_by(SampleSize) %>%
  do({
    data = .
    
    # Check if the data for any group is constant
    group_variances <- data %>%
      group_by(DataType) %>%
      summarise(var = var(Percent_true, na.rm = TRUE))  # Handle NAs in variance calculation
    
    if (any(is.na(group_variances$var)) || any(group_variances$var == 0)) {
      # If data is constant or variance calculation resulted in NA, assign p-value as NA
      p_value <- NA
    } else {
      # Perform t-test
      wilcox <- exactRankTests::wilcox.exact(Percent_true ~ DataType, data = data)
      p_value <- tidy(wilcox)$p.value
    }
    
    summarise(data,
      mean_percent_true_LIMON = mean(Percent_true[DataType == "LIMON"], na.rm = TRUE),
      sd_percent_true_LIMON = sd(Percent_true[DataType == "LIMON"], na.rm = TRUE),
      n_LIMON = sum(DataType == "LIMON", na.rm = TRUE),
      mean_percent_true_SPIECEASI = mean(Percent_true[DataType == "SPIEC-EASI"], na.rm = TRUE),
      sd_percent_true_SPIECEASI = sd(Percent_true[DataType == "SPIEC-EASI"], na.rm = TRUE),
      n_SPIECEASI = sum(DataType == "SPIEC-EASI", na.rm = TRUE),
      p.value = p_value
    )
  }) %>%
  mutate(
    se_percent_true_LIMON = sd_percent_true_LIMON / sqrt(n_LIMON),
    se_percent_true_SPIECEASI = sd_percent_true_SPIECEASI / sqrt(n_SPIECEASI),
    significance = sapply(p.value, p_value_sig)
  )

# Step 2: Make the plotting data
#################################################################
Summary_data_long <- Summary_data %>%
  dplyr::select(SampleSize, starts_with("mean_percent_true"), starts_with("se_percent_true"), significance) %>%
  pivot_longer(cols = starts_with("mean_percent_true"), names_to = "DataType", values_to = "mean_percent_true") %>%
  mutate(DataType = ifelse(str_detect(DataType, "LIMON"), "LIMON", "SPIEC-EASI")) %>%
  pivot_longer(cols = starts_with("se_percent_true"), names_to = "DataType_se", values_to = "se_percent_true") %>%
  mutate(DataType_se = ifelse(str_detect(DataType_se, "LIMON"), "LIMON", "SPIEC-EASI")) %>%
  filter(DataType == DataType_se) %>%
  dplyr::select(-DataType_se)

# Step 3: Final figure
#################################################################
ggplot(Summary_data_long, aes(x = SampleSize, y = mean_percent_true, color = DataType)) +
  geom_line() +
  geom_point() +
  geom_errorbar(aes(ymin = mean_percent_true - se_percent_true, ymax = mean_percent_true + se_percent_true), width = 1.0) +
  geom_text(aes(label = significance, y = 0.9), size = 5, 
            vjust = -0.5, check_overlap = TRUE, color = "black") + # p-value annotations
  labs(x = "SampleSize", y = "Percent_true", color = "DataType") +
  ylim(0, 1) + 
  xlab("Sample Size") +
  ylab("Percent Recovered Edges") +
  scale_color_manual(name = "Network", values = c("LIMON" = "orange", "SPIEC-EASI" = "blue")) +
  scale_x_continuous(limits = c(10, 100), breaks = c(10,20,50,75,100))  +
  theme_classic() +
  theme(axis.text.x = element_text(family = "arial",color = "black", size = 18),
        axis.text.y = element_text(family = "arial",color = "black", size = 18),
        axis.title.x = element_text(family = "arial",color = "black", size = 14),
        axis.title.y = element_text(family = "arial",color = "black", size = 14))

3.2 Graph Total Edges

3.2.1 - Data Formatting

Make Data frame for Total Edges per data Type by Time point

# Initialize an empty list to store results
total_edges_list <- list()

# Loop through each timepoint from 1 to 10
for (time in 1:10) {
  # Calculate Total Edges for the current timepoint
  total_edges <- Subject_sens_data_full %>%
    filter(Time == time) %>%
    group_by(SampleSize) %>%
    summarize(
      Count_L_Edge_weight = sum(!is.na(L_Edge_weight)),
      Count_O_Edge_weight = sum(!is.na(O_Edge_weight)),
      Count_Cov_Edge_weight = sum(!is.na(Cov_Edge_weight))
    ) %>%
    pivot_longer(cols = starts_with("Count"), names_to = "Counts_Column_Name", values_to = "Count") %>%
    mutate(DataType = case_when(
      Counts_Column_Name == "Count_L_Edge_weight" ~ "LIMON",
      Counts_Column_Name == "Count_O_Edge_weight" ~ "True",
      Counts_Column_Name == "Count_Cov_Edge_weight" ~ "SPIEC-EASI"
    ))
  total_edges$Time <- time
  
  # Store the result in the list
  total_edges_list[[time]] <- total_edges
}

# Combine all timepoints together
Total_Edges <- do.call(rbind, total_edges_list)

3.2.2 - Statistics

Check normalcy to determine if t-test or if wilcox rank sum

# Step 1 - Run Shapiro Wilks to test for normalacy of the data
#############################################################################
shapiro_result <- shapiro.test(Total_Edges$Count)
p_value <- shapiro_result$p.value

# Step 2 - Print p-value on the histogram
#############################################################################
# Create the histogram plot
hist(Total_Edges$Count, breaks=100, 
     main = "Total Edges", 
     xlab = "Count of edges", ylab = "Frequency")
# Add the p-value annotation
text(x = 100, y = 10, 
     labels = paste("Shapiro-Wilk p-value: ", p_value))

Data is not normally distributed, use Wilcox Rank sum isntead of T-test

# Step 1: Summary Statistics
#################################################################
Summary_data <- Total_Edges %>%
  group_by(SampleSize) %>%
  summarise(
    # LIMON
    mean_edges_LIMON = mean(Count[DataType == "LIMON"], na.rm = TRUE),
    sd_edges_LIMON = sd(Count[DataType == "LIMON"], na.rm = TRUE),
    n_LIMON = sum(DataType == "LIMON", na.rm = TRUE),
    
    # SPIEC-EASI + COV
    mean_edges_SPIECEASI = mean(Count[DataType == "SPIEC-EASI"], na.rm = TRUE),
    sd_edges_SPIECEASI = sd(Count[DataType == "SPIEC-EASI"], na.rm = TRUE),
    n_SPIECEASI = sum(DataType == "SPIEC-EASI", na.rm = TRUE),
    
    # SPIEC-EASI no COV
    mean_edges_TRUE = mean(Count[DataType == "True"], na.rm = TRUE),
    sd_edges_TRUE = sd(Count[DataType == "True"], na.rm = TRUE),
    n_TRUE = sum(DataType == "True", na.rm = TRUE),
    
    # p-values
    p_value_LIMON_TRUE = if (n_LIMON > 0 & n_TRUE > 0) 
      tidy(exactRankTests::wilcox.exact(Count[DataType %in% c("LIMON", "True")] ~ 
                                          DataType[DataType %in% c("LIMON", "True")], 
                                        exact = FALSE))$p.value else NA,
    p_value_LIMON_SPIECEASI = if (n_LIMON > 0 & n_SPIECEASI > 0) 
      tidy(exactRankTests::wilcox.exact(Count[DataType %in% c("LIMON", "SPIEC-EASI")] ~ 
                                          DataType[DataType %in% c("LIMON", "SPIEC-EASI")], 
                       exact = FALSE))$p.value else NA,
                       p_value_SPIECEASI_TRUE = if (n_SPIECEASI > 0 & n_TRUE > 0) 
      tidy(exactRankTests::wilcox.exact(Count[DataType %in% c("SPIEC-EASI", "True")] ~ 
                                          DataType[DataType %in% c("SPIEC-EASI", "True")], 
                       exact = FALSE))$p.value else NA
  ) %>%
  mutate(
    se_edges_LIMON = sd_edges_LIMON / sqrt(n_LIMON),
    se_edges_SPIECEASI = sd_edges_SPIECEASI / sqrt(n_SPIECEASI),
    se_edges_TRUE = sd_edges_TRUE / sqrt(n_TRUE),
    significance_LIMON_TRUE = sapply(p_value_LIMON_TRUE, p_value_sig),
    significance_LIMON_SPIECEASI = sapply(p_value_LIMON_SPIECEASI, p_value_sig),
    significance_SPIECEASI_TRUE = sapply(p_value_SPIECEASI_TRUE, p_value_sig)
  )

# Step 2: Make the plotting data
#################################################################
Summary_data_long <- Summary_data %>%
  dplyr::select(SampleSize, starts_with("mean_edges"), starts_with("se_edges"), starts_with("significance")) %>%
  pivot_longer(cols = starts_with("mean_edges"), names_to = "DataType", values_to = "mean_edges") %>%
  mutate(DataType = case_when(
    str_detect(DataType, "LIMON") ~ "LIMON",
    str_detect(DataType, "SPIECEASI") ~ "SPIEC-EASI",
    str_detect(DataType, "TRUE") ~ "True"
  )) %>%
  pivot_longer(cols = starts_with("se_edges"), names_to = "DataType_se", values_to = "se_edges") %>%
  mutate(DataType_se = case_when(
    str_detect(DataType_se, "LIMON") ~ "LIMON",
    str_detect(DataType_se, "SPIECEASI") ~ "SPIEC-EASI",
    str_detect(DataType_se, "TRUE") ~ "True"
  )) %>%
  filter(DataType == DataType_se) %>%
  dplyr::select(-DataType_se)

# Add significance labels
Summary_data_long <- Summary_data_long %>%
  left_join(
    Summary_data %>% dplyr::select(SampleSize, 
                                   significance_LIMON_TRUE, 
                                   significance_LIMON_SPIECEASI, 
                                   significance_SPIECEASI_TRUE),
    by = "SampleSize"
  )
# Step 3: Final figure
#################################################################
ggplot(Summary_data_long, aes(x = SampleSize, y = mean_edges, color = DataType)) +
  geom_line() +
  geom_point() +
  
  # Add Error bars
  geom_errorbar(aes(ymin = mean_edges - se_edges, 
                    ymax = mean_edges + se_edges), width = 1.0) +
  
    # Add a legend to describe all the colors
  scale_color_manual(name = "Network", 
                     values = c("LIMON" = "orange", 
                                "SPIEC-EASI" = "blue", 
                                "True" = "grey"),
                     labels = c("LIMON" = "LIMON", 
                                "SPIEC-EASI" = "SPIEC-EASI + Covariates", 
                                "True" = "SPIEC-EASI - No Covariates")) +
  
  # Use ggnewscale to define a new color scale after the first one
  ggnewscale::new_scale_color() +
  
  # Annotate LIMON vs TRUE significance level
  geom_text(aes(label = significance_LIMON_TRUE.x, y = (mean_edges + se_edges + 5), 
                color = factor("LIMON-True")), 
            size = 5, vjust = -0.5, check_overlap = TRUE, 
            data = Summary_data_long %>% filter(DataType == "LIMON")) + 
  
  # Annotate LIMON vs SPIEC-EASI significance level
  geom_text(aes(label = significance_LIMON_SPIECEASI.x, y = (mean_edges + se_edges + 15), 
                color = factor("LIMON-SPIECEASI")), 
            size = 5, vjust = -0.5, check_overlap = TRUE, 
            data = Summary_data_long %>% filter(DataType == "LIMON")) + 
  
  # Annotate SPIEC-EASI vs TRUE significance level
  geom_text(aes(label = significance_SPIECEASI_TRUE.x, y = (mean_edges + se_edges + 8), 
                color = factor("SPIECEASI-True")), 
            size = 5, vjust = -0.5, check_overlap = TRUE, 
            data = Summary_data_long %>% filter(DataType == "SPIEC-EASI")) + 
  
  # Add on labels and themes
  labs(x = "Sample Size", y = "Total Network Edges") +
  ylim(0,150) +
  scale_x_continuous(limits = c(10, 100), breaks = c(10,20,50,75,100))  +
  theme_classic() +
  theme(axis.text.x = element_text(family = "arial",color = "black", size = 18),
        axis.text.y = element_text(family = "arial",color = "black", size = 18),
        axis.title.x = element_text(family = "arial",color = "black", size = 14),
        axis.title.y = element_text(family = "arial",color = "black", size = 14),
        legend.text = element_text(size = 10),   # Increases legend text size
        legend.title = element_text(size = 12)) +
  
  # Add a legend to describe all the colors
  scale_color_manual(name = "Wilcoxon signficance", 
                     values = c("LIMON-True" = "darkorange2", 
                                "LIMON-SPIECEASI" = "black",
                                "SPIECEASI-True" = "deepskyblue"),
                     labels = c("LIMON-True" = "LIMON*No Cov", 
                                "LIMON-SPIECEASI" = "LIMON*SPIECEASI",
                                "SPIECEASI-True" = "SPIECEASI*No Cov"))

3.3 Strength of Recovered Edges

3.3.1 - Data Formatting

Read back in the raw data with no covariates, get the spearman correlation per time point (1,2,3,4,5) and per sample size level (10,20,50,75,100). We will then compare these strength of recovered edges to the edges recovered by our various tests above.

Raw10 <- read.csv(here("Data", "GLV_SimData", "Dataset_2","GLV_N10.csv")) %>% 
                                          column_to_rownames("X") %>% 
                                          dplyr::select(-c(Time, ID, Sex, BMI, Age))
Raw20 <- read.csv(here("Data", "GLV_SimData", "Dataset_2","GLV_N20.csv")) %>% 
                                          column_to_rownames("X") %>% 
                                          dplyr::select(-c(Time, ID, Sex, BMI, Age))
Raw50 <- read.csv(here("Data", "GLV_SimData", "Dataset_2","GLV_N50.csv")) %>% 
                                          column_to_rownames("X") %>% 
                                          dplyr::select(-c(Time, ID, Sex, BMI, Age))
Raw75 <- read.csv(here("Data", "GLV_SimData", "Dataset_2","GLV_N75.csv")) %>% 
                                          column_to_rownames("X") %>% 
                                          dplyr::select(-c(Time, ID, Sex, BMI, Age))
Raw100 <- read.csv(here("Data", "GLV_SimData", "Dataset_2","GLV_N100.csv")) %>% 
                                          column_to_rownames("X") %>% 
                                          dplyr::select(-c(Time, ID, Sex, BMI, Age))
# Define sample sizes and corresponding data frames
sample_sizes <- c(10, 20, 50, 75, 100)
data_frames <- list(Raw10, Raw20, Raw50, Raw75, Raw100)

# Initialize an empty list to store results for each sample size
cov_results <- list()

for (i in seq_along(sample_sizes)) {
  df <- data_frames[[i]]
  sample_size <- sample_sizes[i]
  
  df$Subject <- sub("_Time[0-9]+", "", rownames(df))
  df$Timepoint <- sub("Sbj[0-9]+_", "", rownames(df))
  df$Timepoint <- gsub("Time", "", df$Timepoint)
  
  # List of unique timepoints
  timepoints <- unique(df$Timepoint)
  
  # Initialize an empty data frame to store results
  result_df <- data.frame(Source = character(),
                          Sink = character(),
                          Covariance = numeric(),
                          Timepoint = character(),
                          stringsAsFactors = FALSE)
  
  # Loop through each timepoint
  for (time in timepoints) {
    # Subset data for the current timepoint
    df_time <- df %>% filter(Timepoint == time) %>% dplyr::select(-Subject, -Timepoint)
    
    # Calculate covariance matrix for the current timepoint
    cov_matrix <- cor(df_time, method = "spearman")
    
    # Convert the covariance matrix to a long format
    cov_long <- as.data.frame(as.table(cov_matrix))
    
    # dplyr::rename columns for clarity
    names(cov_long) <- c("Sink", "Source",  "Covariance")
    
    # Add timepoint information
    cov_long$Timepoint <- time
    
    # Combine with the result data frame
    result_df <- bind_rows(result_df, cov_long)
  }
  
  # Add sample size information
  result_df <- result_df %>% dplyr::rename("Time" ="Timepoint")
  result_df$SampleSize <- sample_size
  
  # Store in the list
  cov_results[[i]] <- result_df
}

# Merge all results into one data frame
True_cov <- do.call(rbind, cov_results)

Merge together and add annotaiton for strength of edges detected
- the x axis will be a range from the “Covariance” column with edges between -1 - -0.5, -0.5 - -0.1, -0.1 - 0.1, 0.1 -0.5, 0.5 - 1.
- the y axis will be what percentage of the edges detected for that network/method fall into each of these categories

# Merge with my data
Strength_data <- merge(True_cov, Subject_sens_data_taxa, by.y = c("Source", "Sink", "Time", "SampleSize"), all = TRUE)

# Add in columns based on what strength of association it detected
Strength_data <- Strength_data %>% 
  # for LIMON recovery
  mutate(LIMON_strength = case_when(
    !is.na(L_Edge_weight) & Covariance >= -1 & Covariance < -0.5 ~ "-1 - -0.5",
    !is.na(L_Edge_weight) & Covariance >= -0.5 & Covariance < -0.1 ~ "-0.5 - -0.1",
    !is.na(L_Edge_weight) & Covariance >= -0.1 & Covariance < 0.1 ~ "-0.1 - 0.1",
    !is.na(L_Edge_weight) & Covariance >= 0.1 & Covariance < 0.5 ~ "0.1 - 0.5",
    !is.na(L_Edge_weight) & Covariance >= 0.5 & Covariance <= 1 ~ "0.5 - 1",
    TRUE ~ as.character(NA)  
  )) %>% 
  # for SPIEC-EASI recovery
  mutate(SPIECEASI_strength = case_when(
    !is.na(Cov_Edge_weight) & Covariance >= -1 & Covariance < -0.5 ~ "-1 - -0.5",
    !is.na(Cov_Edge_weight) & Covariance >= -0.5 & Covariance < -0.1 ~ "-0.5 - -0.1",
    !is.na(Cov_Edge_weight) & Covariance >= -0.1 & Covariance < 0.1 ~ "-0.1 - 0.1",
    !is.na(Cov_Edge_weight) & Covariance >= 0.1 & Covariance < 0.5 ~ "0.1 - 0.5",
    !is.na(Cov_Edge_weight) & Covariance >= 0.5 & Covariance <= 1 ~ "0.5 - 1",
    TRUE ~ as.character(NA)  
  ))

# Split into True Positives Dataset and False Positives Datasets
True_pos_raw <- Strength_data %>% filter(!is.na(O_Edge_weight)) %>%
                                  filter(!(is.na(L_Edge_weight) & is.na(Cov_Edge_weight)))
False_pos_raw <- Strength_data %>% filter(is.na(O_Edge_weight)) %>%
                                  filter(!(is.na(L_Edge_weight) & is.na(Cov_Edge_weight)))

3.3.2 - Statistics

True Positives

# Step 1 turn to long format
###############################################################
long_data <- True_pos_raw %>%
  pivot_longer(cols = c("LIMON_strength", "SPIECEASI_strength"),
               names_to = "Network",
               values_to = "Weight",
               values_drop_na = TRUE) %>%
  mutate(Network = case_when(
    Network == "LIMON_strength" ~ "LIMON", 
    Network == "SPIECEASI_strength" ~ "SPIECEASI")) 


# Step 2: Find total networks per Network type and sample size
####################################################################
summary_data <- long_data %>%
  group_by(Network, SampleSize) %>%
  mutate(Total_edge = sum(!is.na(Weight)))

# Step 3: Turn to percentages averaging by time to plot
####################################################################
summary_data2 <- summary_data %>%
  group_by(Network, SampleSize) %>%
  mutate(total_count = n()) %>%
  group_by(Network, SampleSize, Weight) %>%
  summarize(count = n(),
            mean_percentage = (count / dplyr::first(total_count)), 
            .groups = 'drop')


# Step 4: Plot
####################################################################
# Stacked + percent
ggplot(summary_data2, aes(fill=Weight, y=mean_percentage, x=as.factor(SampleSize))) + 
  geom_bar(position = position_dodge(width = 0.8), stat = "identity", width = 0.7) + 
  facet_wrap(~ Network) + 
  scale_fill_manual(name = "Edge Weight",
                    values = c("-1 - -0.5" = "darkred",
                               "-0.5 - -0.1" = "indianred1",
                               "-0.1 - 0.1" = "moccasin",
                               "0.1 - 0.5" = "lightblue",
                               "0.5 - 1" = "darkblue"),
                    breaks = c("-1 - -0.5",
                               "-0.5 - -0.1",
                               "-0.1 - 0.1",
                               "0.1 - 0.5",
                               "0.5 - 1",
                               "NA")) +
  # Add on labels and themes
  labs(x = "Sample Size", y = "Percent True Positives") +
  ylim(0,1) +
  scale_x_discrete() +
  theme_linedraw() +
  theme(axis.text.x = element_text(family = "arial",color = "black", size = 14),
        axis.text.y = element_text(family = "arial",color = "black", size = 14),
        axis.title.x = element_text(family = "arial",color = "black", size = 14),
        axis.title.y = element_text(family = "arial",color = "black", size = 14),
        strip.text = element_text(face = "bold", family = "arial", color = "white", size = 12))

ggplot(summary_data2, aes(x = SampleSize, y = mean_percentage, fill = Weight)) + 
  geom_density(position = "fill", stat = "identity", alpha = 0.6) +
  facet_wrap(~ Network) + 
  scale_fill_manual(name = "Edge Weight",
                    values = c("-1 - -0.5" = "darkred",
                               "-0.5 - -0.1" = "indianred1",
                               "-0.1 - 0.1" = "moccasin",
                               "0.1 - 0.5" = "lightblue",
                               "0.5 - 1" = "darkblue"),
                    breaks = c("-1 - -0.5",
                               "-0.5 - -0.1",
                               "-0.1 - 0.1",
                               "0.1 - 0.5",
                               "0.5 - 1",
                               "NA")) +
  # Add on labels and themes
  labs(x = "Sample Size", y = "Density (Proportion of True Positives)") +
  scale_x_continuous(limits = c(0, 110), breaks = c(10,20,50,75,100))  +
  theme_linedraw() +
  theme(axis.text.x = element_text(family = "arial",color = "black", size = 12),
        axis.text.y = element_text(family = "arial",color = "black", size = 12),
        axis.title.x = element_text(family = "arial",color = "black", size = 14),
        axis.title.y = element_text(family = "arial",color = "black", size = 14),
        strip.text = element_text(face = "bold", family = "arial", color = "white", size = 12))

False Positives

# Step 1 turn to long format
###############################################################
long_data <- False_pos_raw %>%
  pivot_longer(cols = c("LIMON_strength", "SPIECEASI_strength"),
               names_to = "Network",
               values_to = "Weight",
               values_drop_na = TRUE) %>%
  mutate(Network = case_when(
    Network == "LIMON_strength" ~ "LIMON", 
    Network == "SPIECEASI_strength" ~ "SPIECEASI")) 


# Step 2: Find total networks per Network type and sample size
####################################################################
summary_data <- long_data %>%
  group_by(Network, SampleSize) %>%
  mutate(Total_edge = sum(!is.na(Weight)))

# Step 3: Turn to percentages averaging by time to plot
####################################################################
summary_data2 <- summary_data %>%
  group_by(Network, SampleSize) %>%
  mutate(total_count = n()) %>%
  group_by(Network, SampleSize, Weight) %>%
  summarize(count = n(),
            mean_percentage = (count / dplyr::first(total_count)), 
            .groups = 'drop')


# Step 4: Plot
####################################################################
# Stacked + percent
ggplot(summary_data2, aes(fill=Weight, y=mean_percentage, x=as.factor(SampleSize))) + 
  geom_bar(position = position_dodge(width = 0.8), stat = "identity", width = 0.7) + 
  facet_wrap(~ Network) + 
  scale_fill_manual(name = "Edge Weight",
                    values = c("-1 - -0.5" = "darkred",
                               "-0.5 - -0.1" = "indianred1",
                               "-0.1 - 0.1" = "moccasin",
                               "0.1 - 0.5" = "lightblue",
                               "0.5 - 1" = "darkblue"),
                    breaks = c("-1 - -0.5",
                               "-0.5 - -0.1",
                               "-0.1 - 0.1",
                               "0.1 - 0.5",
                               "0.5 - 1",
                               "NA")) +
  # Add on labels and themes
  labs(x = "Sample Size", y = "Percent False Positives") +
  ylim(0,1) +
  scale_x_discrete() +
  theme_linedraw() +
  theme(axis.text.x = element_text(family = "arial",color = "black", size = 14),
        axis.text.y = element_text(family = "arial",color = "black", size = 14),
        axis.title.x = element_text(family = "arial",color = "black", size = 14),
        axis.title.y = element_text(family = "arial",color = "black", size = 14),
        strip.text = element_text(face = "bold", family = "arial", color = "white", size = 12))

4 - Notifications

#system("say Your Silly code finished!")
LS0tCnRpdGxlOiAiRGF0YXNldCAyIC0gU2Vuc2l0aXZpdHkgb2YgTnVtYmVyIG9mIFN1YmplY3RzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpIZXJlIHRlc3QgdGhlIHN0cmVuZ3RoIG9mIHJlY292ZXJ5IG9mIGVkZ2VzIG9mIHRoZSBuZXR3b3JrcyBvbiBjb3ZhcmlhdGUgaW5mbGF0ZWQgZGF0YSBwYXNzZWQgdG8gTElNT04gYW5kIHBhc3NlZCB0byBTUElFQy1FQVNJLiBUaGVzZSB3aWxsIGJlIGNvbXBhcmVkIHRvIG5vbi1jb3ZhcmlhdGUgaW5mbGF0ZWQgZGF0YSBwYXNzZWQgdG8gU1BJRUMtRUFTSS4gV2UgdXRpbGl6ZSB0aGUgTElNT04gU1BJRUMtRUFTSSBmdW5jdGlvbnMgdG8gaXRlcmF0aXZlbHkgZ2VuZXJhdGUgdGhlIG5ldHdvcmtzIGZvciBjb3ZhcmlhdGUgZGF0YSBwYXNzZWQgdG8gU1BJRUMtRUFTSSwgYnV0IGJ5cGFzcyB0aGUgbGluZWFyIG1peGVkIGVmZmVjdCBtb2RlbCBkaXN0cmlidXRpb24gZml0dGluZyBzdGVwIG9mIExJTU9OLiBUaGlzIGlzIHJlcGVhdGVkIDUgdGltZXMgZm9yIGluY3JlYXNlIHN1YmplY3Qgc2FtcGxlIHNpemUgb2YgMTAsIDIwLCA1MCwgNzUsIGFuZCAxMDAuIAoKCiMgMS4gTG9hZCBMaWJyYXJ5CioqKiAKUGFja2FnZXMgcmVxdWlyZWQgdG8gcnVuIHRoZSBzY3JpcHQKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGlncmFwaCkKbGlicmFyeShOQlpJTU0pCmxpYnJhcnkoU3BpZWNFYXNpKQpsaWJyYXJ5KExJTU9OKQpsaWJyYXJ5KGhlcmUpCmxpYnJhcnkobG1lNCkKbGlicmFyeShNYXRyaXgpCmxpYnJhcnkodHNjb3VudCkKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkoTUFTUykKbGlicmFyeShtYXRyaXhjYWxjKQpsaWJyYXJ5KGdyaWRFeHRyYSkKbGlicmFyeShkZXZ0b29scykKbGlicmFyeShtaWFTaW0pCmxpYnJhcnkocmVzaGFwZTIpCmxpYnJhcnkoZ2dwdWJyKQpsaWJyYXJ5KGJyb29tKQpsaWJyYXJ5KGdnbmV3c2NhbGUpCmxpYnJhcnkoY29pbikKYGBgCgojIyAxLjIgLSBGdW5jdGlvbnMKClRoaXMgZnVuY3Rpb24gd2lsbCBiZSB1c2VkIHRvIGRlZmluZSB3aGljaCBlZGdlcyBhcmUgY29uc2lzdGVudCBhY3Jvc3MgZGlmZmVyZW50IG5ldHdvcmtzCmBgYHtyfQplZGdlX3JlY292ZXJ5IDwtIGZ1bmN0aW9uKGRhdGEpIHsKICBkYXRhIDwtIGRhdGEgJT4lCiAgICBtdXRhdGUoCiAgICAgIFJlY292ZXJ5ID0gY2FzZV93aGVuKAogICAgICAgICFpcy5uYShMX0VkZ2Vfd2VpZ2h0KSAmICFpcy5uYShPX0VkZ2Vfd2VpZ2h0KSAmICFpcy5uYShDb3ZfRWRnZV93ZWlnaHQpICYKICAgICAgICAgIChMX0VkZ2Vfd2VpZ2h0ICE9IDAgJiBPX0VkZ2Vfd2VpZ2h0ICE9IDAgJiBDb3ZfRWRnZV93ZWlnaHQgIT0gMCkgfiAzLAogICAgICAgICFpcy5uYShPX0VkZ2Vfd2VpZ2h0KSAmICFpcy5uYShDb3ZfRWRnZV93ZWlnaHQpICYKICAgICAgICAgIChPX0VkZ2Vfd2VpZ2h0ICE9IDAgJiBDb3ZfRWRnZV93ZWlnaHQgIT0gMCkgfiAyLAogICAgICAgICFpcy5uYShMX0VkZ2Vfd2VpZ2h0KSAmICFpcy5uYShPX0VkZ2Vfd2VpZ2h0KSAmCiAgICAgICAgICAoTF9FZGdlX3dlaWdodCAhPSAwICYgT19FZGdlX3dlaWdodCAhPSAwKSB+IDEsCiAgICAgICAgVFJVRSB+IDAKICAgICAgKQogICAgKQogIAogIGRhdGEKfQpgYGAKCgoKIyAyLiBNb2RlbCBUZXN0aW5nCioqKgoKCiMjIDIuMSAtIFN1YmplY3RzIE49MTAKCiMjIyAyLjEuMSAtIExJTU9OIG9uIENvdmFyaWF0ZSBJbmZsYXRlZCBEYXRhClJ1biB0aGUgTElNT04gY292YXJpYXRlIGNvcnJlY3Rpb24gYW5kIG5ldHdvcmsgaW5mZXJlbmNlIHN0ZXBzIG9uIENvdmFyaWF0ZSBpbmZsYXRlZCBkYXRhLiBUaGVzZSBlZGdlcyB3aWxsIGJlIHJlcXVpcmVkIGFzIExfRWRnZXMgIAoKTWFrZSBMSU1PTiBPYmplY3QKYGBge3J9ClNpbURhdGEgPC0gcmVhZC5jc3YoaGVyZSgiRGF0YSIsICJHTFZfU2ltRGF0YSIsICJEYXRhc2V0XzIiLCJHTFZfQ292X04xMC5jc3YiKSkKClNpbURhdGEgPC0gY29sdW1uX3RvX3Jvd25hbWVzKFNpbURhdGEsICJYIikKCiMgbWV0YWRhdGEKbWV0YV9kYXRhIDwtIFNpbURhdGFbLDE6NV0KbWV0YV9kYXRhJFRpbWUgPC0gYXMubnVtZXJpYyhtZXRhX2RhdGEkVGltZSkKbWV0YV9kYXRhJEJNSSA8LSBhcy5udW1lcmljKG1ldGFfZGF0YSRCTUkpCm1ldGFfZGF0YSRBZ2UgPC0gYXMubnVtZXJpYyhtZXRhX2RhdGEkQWdlKQoKIyBPcHRpb24gdG8gc2hvcnRlbiB0byBydW4gZmFzdGVyCmxpbW9uX21ldGEgPC0gbWV0YV9kYXRhICMlPiUgZmlsdGVyKFRpbWUgJWluJSBjKDEsMiwzLDQsNSkpCgojIENvdW50IGRhdGEKbGltb25fY291bnRzIDwtIGFzLm1hdHJpeChTaW1EYXRhWyw2OjU1XSkKCiMgc29ydCByb3dzCmxpbW9uX2NvdW50cyA8LSBsaW1vbl9jb3VudHNbcm93bmFtZXMobGltb25fbWV0YSksXQoKICAKTF9vYmogPC0gTElNT05fT2JqKENvdW50cyA9IGxpbW9uX2NvdW50cywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFNhbXBsZURhdGEgPSBsaW1vbl9tZXRhKQpgYGAKCgoKCgpEaXN0cmlidXRpb24gRml0dGluZyBhbmQgY2hlY2sKYGBge3J9CiMgU2V0IHNlZWQKc2V0LnNlZWQoMTIzNDUpCiMgRml0IHRoZSBkaXN0cmlidXRpb24vcmVtb3ZlIGNvdmFyaWF0ZXMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCkxfb2JqMiA8LSBMSU1PTl9EaXN0ckZpdChPYmogPSBMX29iaiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFRpbWUgPSAiVGltZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICBTdWJqZWN0ID0gIklEIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIENvdmFyaWF0ZXMgPSBjKCJTZXgiLCAiQk1JIiwgIkFnZSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBtb2RlbCA9ICJTZXgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBkaXN0cmlidXRpb24gPSAiTE1NIikKYGBgCgoKCgoKTmV0d29ya3Mgb2YgTElNT04gRGF0YQpgYGB7cn0Kc2V0LnNlZWQoMTIzNDUpCnBzZWVkIDwtIGxpc3QocmVwLm51bT01MCwgc2VlZD0xMDAxMCkKIyBTUElFQy1FQVNJIHBlciB0aW1lCkxfb2JqMyA8LSBMSU1PTl9OZXRJbmZfVGltZShPYmogPSBMX29iajIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJnbGFzc28iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWwuY3JpdGVyaW9uID0gImJzdGFycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFtYmRhLm1pbi5yYXRpbyA9IDAuMDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHVsc2FyLnNlbGVjdD1UUlVFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdWxzYXIucGFyYW1zPXBzZWVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5sYW1iZGEgPSAyMDApCgoKIyBQcmludCBOZXR3b3JrcwpMX29iajQgPC0gTElNT05fRWRnZXNfTmV0d29ya3MoTF9vYmozLCB0aHJlc2hvbGQgPSAwLjAwMDIsIHZlcnRleC5zaXplID0gMywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcnRleC5sYWJlbC5jZXggPSA4LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVydGV4LmxhYmVsLmNvbG9yID0gImJsYWNrIikKYGBgCgoKCiMjIyAyLjEuMiAtIFRydWUgTmV0d29ya3MgLSBubyBDb3ZhcmlhdGVzICAKClNQSUVDLUVBU0kgTmV0d29ya3Mgb2YgdGhlIFJhdyBEYXRhIHdpdGhvdXQgY292YXJpYXRlcy4gVGhlc2Ugd2lsbCBiZSBsYWJlbGVkIGFzIE9fRWRnZXMgKGZvciBvcmlnaW5hbCBlZGdlcykuIFdlIHJlYWQgaW4gdGhlIGRhdGEgYW5kIHRoZW4gYXNzaWduIGl0IHRvIGEgZG93bnN0cmVhbSBMSU1PbiBvYmplY3QgdG8gcnVuIFNQSUVDLUVBU0kgYWNyb3NzIGFsbCB0aGUgdGltZXBvaW50cyBidXQgYnlwYXNzaW5nIHRoZSBuZXR3b3JrIGluZmVyZW5jZSBzdGVwCmBgYHtyfQojIE1ha2UgZmFrZSBMSU1PTiBPYmplY3QgdG8gUGFzcyB0byBOZXR3b3JrIEluZmVyZW5jZSBTdGVwcwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCm9yaWdpbmFsX2RhdGEgPC0gcmVhZC5jc3YoaGVyZSgiRGF0YSIsICJHTFZfU2ltRGF0YSIsICJEYXRhc2V0XzIiLCJHTFZfTjEwLmNzdiIpKQpvcmlnaW5hbF9kYXRhIDwtIG9yaWdpbmFsX2RhdGEgJT4lIGNvbHVtbl90b19yb3duYW1lcygiWCIpCgojIExvb3AgdGhyb3VnaCB0aGUgdGltZSBwb2ludHMsIGZpbHRlciBkYXRhLCBhbmQgcmVtb3ZlIG1ldGFkYXRhCmNvdW50c19saXN0IDwtIGxpc3QoKQpmb3IgKGkgaW4gMToxMCkgewogIGZpbHRlcmVkX2NvdW50cyA8LSBvcmlnaW5hbF9kYXRhICU+JSBmaWx0ZXIoVGltZSA9PSBpKQogIGNvdW50c19saXN0W1tpXV0gPC0gcm91bmQoZmlsdGVyZWRfY291bnRzWywgNjo1NV0pCn0KCiMgSW5pdGlhbGl6ZSBMX29ial9zaW0yCkxfb2JqX3NpbTIgPC0gTF9vYmoyCgojIEFzc2lnbiB0aGUgcHJvY2Vzc2VkIGRhdGEgdG8gdGhlIGNvcnJlc3BvbmRpbmcgc2xvdHMgaW4gTF9vYmpfc2ltMgpmb3IgKGkgaW4gMToxMCkgewogIExfb2JqX3NpbTJbWyJDb3JyZWN0ZWRfQ291bnRzX1RpbWUiXV1bW3Bhc3RlMCgiQ29ycmVjdGVkX0NvdW50c18iLCBpKV1dIDwtIGNvdW50c19saXN0W1tpXV0KfQoKCiMgTmV0d29yayBJbmZlcmVuY2UKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFNldCBzZWVkCnNldC5zZWVkKDEyMzQ1KQpwc2VlZCA8LSBsaXN0KHJlcC5udW09NTAsIHNlZWQ9MTAwMTApCiMgU1BJRUMtRUFTSSBwZXIgdGltZQpMX29ial9zaW0zIDwtIExJTU9OX05ldEluZl9UaW1lKE9iaiA9IExfb2JqX3NpbTIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJnbGFzc28iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWwuY3JpdGVyaW9uID0gImJzdGFycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFtYmRhLm1pbi5yYXRpbyA9IDAuMDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHVsc2FyLnNlbGVjdD1UUlVFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdWxzYXIucGFyYW1zPXBzZWVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5sYW1iZGEgPSAyMDApCgoKIyBQcmludCBOZXR3b3JrcwpMX29ial9zaW00IDwtIExJTU9OX0VkZ2VzX05ldHdvcmtzKExfb2JqX3NpbTMsIHRocmVzaG9sZCA9IDAuMDAwMiwgdmVydGV4LnNpemUgPSAzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVydGV4LmxhYmVsLmNleCA9IDgsIHZlcnRleC5sYWJlbC5jb2xvciA9ICJibGFjayIpCgpgYGAKCgojIyMgMi4xLjMgLSBTUElFQy1FQVNJIG9uIENvdmFyaWF0ZSBEYXRhICAKU2ltaWxhciB0byBwcm9jZXNzIGZvciBvcmlnaW5hbCBuZXR3b3JrcywgZXhjZXB0IG5vdyB3ZSB3aWxsIHJ1biBpdCBvbiBjb3ZhcmlhdGUgY29uZmxhdGVkIGRhdGEuIFRoZXNlIHdpbGwgYmUgbGFiZWxlZCBhcyBDb3ZfRWRnZXMKYGBge3J9CiMgTWFrZSBmYWtlIExJTU9OIE9iamVjdCB0byBQYXNzIHRvIE5ldHdvcmsgSW5mZXJlbmNlIFN0ZXBzCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKb3JpZ2luYWxfZGF0YSA8LSByZWFkLmNzdihoZXJlKCJEYXRhIiwgIkdMVl9TaW1EYXRhIiwgIkRhdGFzZXRfMiIsIkdMVl9Db3ZfTjEwLmNzdiIpKQpvcmlnaW5hbF9kYXRhIDwtIG9yaWdpbmFsX2RhdGEgJT4lIGNvbHVtbl90b19yb3duYW1lcygiWCIpCgojIExvb3AgdGhyb3VnaCB0aGUgdGltZSBwb2ludHMsIGZpbHRlciBkYXRhLCBhbmQgcmVtb3ZlIG1ldGFkYXRhCmNvdW50c19saXN0IDwtIGxpc3QoKQpmb3IgKGkgaW4gMToxMCkgewogIGZpbHRlcmVkX2NvdW50cyA8LSBvcmlnaW5hbF9kYXRhICU+JSBmaWx0ZXIoVGltZSA9PSBpKQogIGNvdW50c19saXN0W1tpXV0gPC0gcm91bmQoZmlsdGVyZWRfY291bnRzWywgNjo1NV0pCn0KCiMgSW5pdGlhbGl6ZSBMX29ial9zaW0yCkxfb2JqX3NpbTIgPC0gTF9vYmoyCgojIEFzc2lnbiB0aGUgcHJvY2Vzc2VkIGRhdGEgdG8gdGhlIGNvcnJlc3BvbmRpbmcgc2xvdHMgaW4gTF9vYmpfc2ltMgpmb3IgKGkgaW4gMToxMCkgewogIExfb2JqX3NpbTJbWyJDb3JyZWN0ZWRfQ291bnRzX1RpbWUiXV1bW3Bhc3RlMCgiQ29ycmVjdGVkX0NvdW50c18iLCBpKV1dIDwtIGNvdW50c19saXN0W1tpXV0KfQoKCgojIE5ldHdvcmsgSW5mZXJlbmNlCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBTZXQgc2VlZApzZXQuc2VlZCgxMjM0NSkKcHNlZWQgPC0gbGlzdChyZXAubnVtPTUwLCBzZWVkPTEwMDEwKQojIFNQSUVDLUVBU0kgcGVyIHRpbWUKTF9vYmpfc2ltMyA8LSBMSU1PTl9OZXRJbmZfVGltZShPYmogPSBMX29ial9zaW0yLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAiZ2xhc3NvIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsLmNyaXRlcmlvbiA9ICJic3RhcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhbWJkYS5taW4ucmF0aW8gPSAwLjAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHB1bHNhci5zZWxlY3Q9VFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHVsc2FyLnBhcmFtcz1wc2VlZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBubGFtYmRhID0gMjAwKQoKCiMgUHJpbnQgTmV0d29ya3MKTF9vYmpfc2ltNSA8LSBMSU1PTl9FZGdlc19OZXR3b3JrcyhMX29ial9zaW0zLCB0aHJlc2hvbGQgPSAwLjAwMDIsIHZlcnRleC5zaXplID0gMywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcnRleC5sYWJlbC5jZXggPSA4LCB2ZXJ0ZXgubGFiZWwuY29sb3IgPSAiYmxhY2siKQoKCgpgYGAKCgoKIyMjIDIuMS40IC0gTWVyZ2UgT3V0Y29tZXMKQ2FsY3VsYXRlIHRoZSBzaW1pbGFyIGVkZ2VzIGFjcm9zcyB0aGUgdGhyZWUgZGF0YSB0eXBlcwpgYGB7cn0KIyBMSU1PTiBEYXRhIC0gRXh0cmFjdCBFZGdlcwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyBJbml0aWFsaXplIGFuIGVtcHR5IGxpc3QgdG8gc3RvcmUgdGhlIGRhdGEgZnJhbWVzCkxfRWRnZXNfTGlzdCA8LSBsaXN0KCkKCiMgTG9vcCB0aHJvdWdoIHRoZSBlZGdlIHRhYmxlcyBhbmQgcHJvY2VzcyBlYWNoIG9uZQpmb3IgKGkgaW4gMToxMCkgewogIGVkZ2VfdGFibGUgPC0gYXMuZGF0YS5mcmFtZShMX29iajRbWyJFZGdlX1RhYmxlIl1dW1twYXN0ZTAoIkVkZ2VfVGFibGVfIiwgaSldXSkKICAjIENoZWNrIGlmIHRoZSBlZGdlIHRhYmxlIGlzIGVtcHR5CiAgaWYgKG5yb3coZWRnZV90YWJsZSkgPT0gMCkgewogICAgIyBDcmVhdGUgYSBkYXRhIGZyYW1lIHdpdGggTkEgdmFsdWVzCiAgICBlZGdlX3RhYmxlIDwtIGRhdGEuZnJhbWUoRWRnZV93ZWlnaHQgPSBOQSwgVGltZSA9IGkpCiAgfSBlbHNlIHsKICAgICMgZHBseXI6OnJlbmFtZSB0aGUgY29sdW1uIGFuZCBhZGQgdGhlIFRpbWUgY29sdW1uCiAgICBlZGdlX3RhYmxlIDwtIGVkZ2VfdGFibGUgJT4lIGRwbHlyOjpyZW5hbWUoIkxfRWRnZV93ZWlnaHQiPSJFZGdlX3dlaWdodCIpCiAgICBlZGdlX3RhYmxlJFRpbWUgPC0gaQogIH0KICAjIEFwcGVuZCB0aGUgcHJvY2Vzc2VkIHRhYmxlIHRvIHRoZSBsaXN0CiAgTF9FZGdlc19MaXN0W1tpXV0gPC0gZWRnZV90YWJsZQp9CiMgQ29tYmluZSBhbGwgdGhlIHByb2Nlc3NlZCB0YWJsZXMgaW50byBhIHNpbmdsZSBkYXRhIGZyYW1lCkxfRWRnZXMgPC0gYmluZF9yb3dzKExfRWRnZXNfTGlzdCkKCiMgT3JpZ2luYWwgRGF0YSArIENvdiAtIEV4dHJhY3QgZWRnZXMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCiMgSW5pdGlhbGl6ZSBhbiBlbXB0eSBsaXN0IHRvIHN0b3JlIHRoZSBkYXRhIGZyYW1lcwpDb3ZfRWRnZXNfTGlzdCA8LSBsaXN0KCkKCiMgTG9vcCB0aHJvdWdoIHRoZSBlZGdlIHRhYmxlcyBhbmQgcHJvY2VzcyBlYWNoIG9uZQpmb3IgKGkgaW4gMToxMCkgewogIGVkZ2VfdGFibGUgPC0gYXMuZGF0YS5mcmFtZShMX29ial9zaW01W1siRWRnZV9UYWJsZSJdXVtbcGFzdGUwKCJFZGdlX1RhYmxlXyIsIGkpXV0pCiAgIyBDaGVjayBpZiB0aGUgZWRnZSB0YWJsZSBpcyBlbXB0eQogIGlmIChucm93KGVkZ2VfdGFibGUpID09IDApIHsKICAgICMgQ3JlYXRlIGEgZGF0YSBmcmFtZSB3aXRoIE5BIHZhbHVlcwogICAgZWRnZV90YWJsZSA8LSBkYXRhLmZyYW1lKEVkZ2Vfd2VpZ2h0ID0gTkEsIFRpbWUgPSBpKQogIH0gZWxzZSB7CiAgICAjIGRwbHlyOjpyZW5hbWUgdGhlIGNvbHVtbiBhbmQgYWRkIHRoZSBUaW1lIGNvbHVtbgogICAgZWRnZV90YWJsZSA8LSBlZGdlX3RhYmxlICU+JSBkcGx5cjo6cmVuYW1lKCJDb3ZfRWRnZV93ZWlnaHQiPSJFZGdlX3dlaWdodCIpCiAgICBlZGdlX3RhYmxlJFRpbWUgPC0gaQogIH0KICAjIEFwcGVuZCB0aGUgcHJvY2Vzc2VkIHRhYmxlIHRvIHRoZSBsaXN0CiAgQ292X0VkZ2VzX0xpc3RbW2ldXSA8LSBlZGdlX3RhYmxlCn0KIyBDb21iaW5lIGFsbCB0aGUgcHJvY2Vzc2VkIHRhYmxlcyBpbnRvIGEgc2luZ2xlIGRhdGEgZnJhbWUKQ292X0VkZ2VzIDwtIGJpbmRfcm93cyhDb3ZfRWRnZXNfTGlzdCkKCiMgT3JpZ2luYWwgRGF0YSAtIEV4dHJhY3QgRWRnZXMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBJbml0aWFsaXplIGFuIGVtcHR5IGxpc3QgdG8gc3RvcmUgdGhlIGRhdGEgZnJhbWVzCk9fRWRnZXNfTGlzdCA8LSBsaXN0KCkKCiMgTG9vcCB0aHJvdWdoIHRoZSBlZGdlIHRhYmxlcyBhbmQgcHJvY2VzcyBlYWNoIG9uZQpmb3IgKGkgaW4gMToxMCkgewogIGVkZ2VfdGFibGUgPC0gYXMuZGF0YS5mcmFtZShMX29ial9zaW00W1siRWRnZV9UYWJsZSJdXVtbcGFzdGUwKCJFZGdlX1RhYmxlXyIsIGkpXV0pCiAgIyBDaGVjayBpZiB0aGUgZWRnZSB0YWJsZSBpcyBlbXB0eQogIGlmIChucm93KGVkZ2VfdGFibGUpID09IDApIHsKICAgICMgQ3JlYXRlIGEgZGF0YSBmcmFtZSB3aXRoIE5BIHZhbHVlcwogICAgZWRnZV90YWJsZSA8LSBkYXRhLmZyYW1lKEVkZ2Vfd2VpZ2h0ID0gTkEsIFRpbWUgPSBpKQogIH0gZWxzZSB7CiAgICAjIGRwbHlyOjpyZW5hbWUgdGhlIGNvbHVtbiBhbmQgYWRkIHRoZSBUaW1lIGNvbHVtbgogICAgZWRnZV90YWJsZSA8LSBlZGdlX3RhYmxlICU+JSBkcGx5cjo6cmVuYW1lKCJPX0VkZ2Vfd2VpZ2h0Ij0iRWRnZV93ZWlnaHQiKQogICAgZWRnZV90YWJsZSRUaW1lIDwtIGkKICB9CiAgIyBBcHBlbmQgdGhlIHByb2Nlc3NlZCB0YWJsZSB0byB0aGUgbGlzdAogIE9fRWRnZXNfTGlzdFtbaV1dIDwtIGVkZ2VfdGFibGUKfQojIENvbWJpbmUgYWxsIHRoZSBwcm9jZXNzZWQgdGFibGVzIGludG8gYSBzaW5nbGUgZGF0YSBmcmFtZQpPX0VkZ2VzIDwtIGJpbmRfcm93cyhPX0VkZ2VzX0xpc3QpCgojIEJpbmQgZGF0YSB0b2dldGhlcgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpNZXJnZWRfVDEgPC0gbWVyZ2UoTF9FZGdlcywgT19FZGdlcywgYnkueSA9IGMoIlNvdXJjZSIsICJTaW5rIiwgIlRpbWUiKSwgYWxsPVRSVUUpCk1lcmdlZF9UMSA8LSBtZXJnZShNZXJnZWRfVDEsIENvdl9FZGdlcywgYnkueSA9IGMoIlNvdXJjZSIsICJTaW5rIiwgIlRpbWUiKSwgYWxsPVRSVUUpCgoKIyBDb21wYXJpc29uIEdyYXBoIERhdGEKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCiMgSWRlbnRpZnkgdHJ1ZSBlZGdlcwpNZXJnZWRfVDEwIDwtIE1lcmdlZF9UMSAlPiUKICBlZGdlX3JlY292ZXJ5KCkgCgojIEFkZCBTYW1wbGVTaXplIApNZXJnZWRfVDEwJFNhbXBsZVNpemUgPC0gMTAKYGBgCgoKCgoKIyMgMi4yIC0gU3ViamVjdHMgTj0yMAoKIyMjIDIuMi4xIC0gTElNT04gb24gQ292YXJpYXRlIEluZmxhdGVkIERhdGEKUnVuIHRoZSBMSU1PTiBjb3ZhcmlhdGUgY29ycmVjdGlvbiBhbmQgbmV0d29yayBpbmZlcmVuY2Ugc3RlcHMgb24gQ292YXJpYXRlIGluZmxhdGVkIGRhdGEuIFRoZXNlIGVkZ2VzIHdpbGwgYmUgcmVxdWlyZWQgYXMgTF9FZGdlcyAgCgoKTWFrZSBMSU1PTiBPYmplY3QKYGBge3J9ClNpbURhdGEgPC0gcmVhZC5jc3YoaGVyZSgiRGF0YSIsICJHTFZfU2ltRGF0YSIsICJEYXRhc2V0XzIiLCJHTFZfQ292X04yMC5jc3YiKSkKU2ltRGF0YSA8LSBjb2x1bW5fdG9fcm93bmFtZXMoU2ltRGF0YSwgIlgiKQoKIyBtZXRhZGF0YQptZXRhX2RhdGEgPC0gU2ltRGF0YVssMTo1XQptZXRhX2RhdGEkVGltZSA8LSBhcy5udW1lcmljKG1ldGFfZGF0YSRUaW1lKQptZXRhX2RhdGEkQk1JIDwtIGFzLm51bWVyaWMobWV0YV9kYXRhJEJNSSkKbWV0YV9kYXRhJEFnZSA8LSBhcy5udW1lcmljKG1ldGFfZGF0YSRBZ2UpCgojIE9wdGlvbiB0byBzaG9ydGVuIHRvIHJ1biBmYXN0ZXIKbGltb25fbWV0YSA8LSBtZXRhX2RhdGEgIyU+JSBmaWx0ZXIoVGltZSAlaW4lIGMoMSwyLDMsNCw1KSkKCiMgQ291bnQgZGF0YQpsaW1vbl9jb3VudHMgPC0gYXMubWF0cml4KFNpbURhdGFbLDY6NTVdKQoKIyBzb3J0IHJvd3MKbGltb25fY291bnRzIDwtIGxpbW9uX2NvdW50c1tyb3duYW1lcyhsaW1vbl9tZXRhKSxdCgogIApMX29iaiA8LSBMSU1PTl9PYmooQ291bnRzID0gbGltb25fY291bnRzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgU2FtcGxlRGF0YSA9IGxpbW9uX21ldGEpCmBgYAoKCkRpc3RyaWJ1dGlvbiBGaXR0aW5nIGFuZCBjaGVjawpgYGB7cn0KIyBTZXQgc2VlZApzZXQuc2VlZCgxMjM0NSkKIyBGaXQgdGhlIGRpc3RyaWJ1dGlvbi9yZW1vdmUgY292YXJpYXRlcwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKTF9vYmoyIDwtIExJTU9OX0Rpc3RyRml0KE9iaiA9IExfb2JqLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgVGltZSA9ICJUaW1lIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFN1YmplY3QgPSAiSUQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgQ292YXJpYXRlcyA9IGMoIlNleCIsICJCTUkiLCAiQWdlIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGVsID0gIlNleCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpc3RyaWJ1dGlvbiA9ICJMTU0iKQoKYGBgCgoKTmV0d29ya3Mgb2YgTElNT04gRGF0YQpgYGB7cn0KIyBTZXQgc2VlZApzZXQuc2VlZCgxMjM0NSkKcHNlZWQgPC0gbGlzdChyZXAubnVtPTUwLCBzZWVkPTEwMDEwKQoKIyBTUElFQy1FQVNJIHBlciB0aW1lCkxfb2JqMyA8LSBMSU1PTl9OZXRJbmZfVGltZShPYmogPSBMX29iajIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJnbGFzc28iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWwuY3JpdGVyaW9uID0gImJzdGFycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFtYmRhLm1pbi5yYXRpbyA9IDAuMDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHVsc2FyLnNlbGVjdD1UUlVFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdWxzYXIucGFyYW1zPXBzZWVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5sYW1iZGEgPSAyMDApCgoKIyBQcmludCBOZXR3b3JrcwpMX29iajQgPC0gTElNT05fRWRnZXNfTmV0d29ya3MoTF9vYmozLCB0aHJlc2hvbGQgPSAwLjAwMDIsIHZlcnRleC5zaXplID0gMywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcnRleC5sYWJlbC5jZXggPSA4LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVydGV4LmxhYmVsLmNvbG9yID0gImJsYWNrIikKYGBgCgojIyMgMi4yLjIgLSBUcnVlIE5ldHdvcmtzIC0gbm8gQ292YXJpYXRlcyAgCgpTUElFQy1FQVNJIE5ldHdvcmtzIG9mIHRoZSBSYXcgRGF0YSB3aXRob3V0IGNvdmFyaWF0ZXMuIFRoZXNlIHdpbGwgYmUgbGFiZWxlZCBhcyBPX0VkZ2VzIChmb3Igb3JpZ2luYWwgZWRnZXMpLiBXZSByZWFkIGluIHRoZSBkYXRhIGFuZCB0aGVuIGFzc2lnbiBpdCB0byBhIGRvd25zdHJlYW0gTElNT24gb2JqZWN0IHRvIHJ1biBTUElFQy1FQVNJIGFjcm9zcyBhbGwgdGhlIHRpbWVwb2ludHMgYnV0IGJ5cGFzc2luZyB0aGUgbmV0d29yayBpbmZlcmVuY2Ugc3RlcApgYGB7cn0KIyBNYWtlIGZha2UgTElNT04gT2JqZWN0IHRvIFBhc3MgdG8gTmV0d29yayBJbmZlcmVuY2UgU3RlcHMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpvcmlnaW5hbF9kYXRhIDwtIHJlYWQuY3N2KGhlcmUoIkRhdGEiLCAiR0xWX1NpbURhdGEiLCAiRGF0YXNldF8yIiwiR0xWX04yMC5jc3YiKSkKb3JpZ2luYWxfZGF0YSA8LSBvcmlnaW5hbF9kYXRhICU+JSBjb2x1bW5fdG9fcm93bmFtZXMoIlgiKQoKIyBMb29wIHRocm91Z2ggdGhlIHRpbWUgcG9pbnRzLCBmaWx0ZXIgZGF0YSwgYW5kIHJlbW92ZSBtZXRhZGF0YQpjb3VudHNfbGlzdCA8LSBsaXN0KCkKZm9yIChpIGluIDE6MTApIHsKICBmaWx0ZXJlZF9jb3VudHMgPC0gb3JpZ2luYWxfZGF0YSAlPiUgZmlsdGVyKFRpbWUgPT0gaSkKICBjb3VudHNfbGlzdFtbaV1dIDwtIHJvdW5kKGZpbHRlcmVkX2NvdW50c1ssIDY6NTVdKQp9CgojIEluaXRpYWxpemUgTF9vYmpfc2ltMgpMX29ial9zaW0yIDwtIExfb2JqMgoKIyBBc3NpZ24gdGhlIHByb2Nlc3NlZCBkYXRhIHRvIHRoZSBjb3JyZXNwb25kaW5nIHNsb3RzIGluIExfb2JqX3NpbTIKZm9yIChpIGluIDE6MTApIHsKICBMX29ial9zaW0yW1siQ29ycmVjdGVkX0NvdW50c19UaW1lIl1dW1twYXN0ZTAoIkNvcnJlY3RlZF9Db3VudHNfIiwgaSldXSA8LSBjb3VudHNfbGlzdFtbaV1dCn0KCgojIE5ldHdvcmsgSW5mZXJlbmNlCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBTZXQgc2VlZApzZXQuc2VlZCgxMjM0NSkKcHNlZWQgPC0gbGlzdChyZXAubnVtPTUwLCBzZWVkPTEwMDEwKQojIFNQSUVDLUVBU0kgcGVyIHRpbWUKTF9vYmpfc2ltMyA8LSBMSU1PTl9OZXRJbmZfVGltZShPYmogPSBMX29ial9zaW0yLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAiZ2xhc3NvIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsLmNyaXRlcmlvbiA9ICJic3RhcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhbWJkYS5taW4ucmF0aW8gPSAwLjAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHB1bHNhci5zZWxlY3Q9VFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHVsc2FyLnBhcmFtcz1wc2VlZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBubGFtYmRhID0gMjAwKQoKCiMgUHJpbnQgTmV0d29ya3MKTF9vYmpfc2ltNCA8LSBMSU1PTl9FZGdlc19OZXR3b3JrcyhMX29ial9zaW0zLCB0aHJlc2hvbGQgPSAwLjAwMDIsIHZlcnRleC5zaXplID0gMywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcnRleC5sYWJlbC5jZXggPSA4LCB2ZXJ0ZXgubGFiZWwuY29sb3IgPSAiYmxhY2siKQoKCgpgYGAKCgoKIyMjIDIuMi4zIC0gU1BJRUMtRUFTSSBvbiBDb3ZhcmlhdGUgRGF0YSAgClNpbWlsYXIgdG8gcHJvY2VzcyBmb3Igb3JpZ2luYWwgbmV0d29ya3MsIGV4Y2VwdCBub3cgd2Ugd2lsbCBydW4gaXQgb24gY292YXJpYXRlIGNvbmZsYXRlZCBkYXRhLiBUaGVzZSB3aWxsIGJlIGxhYmVsZWQgYXMgQ292X0VkZ2VzCmBgYHtyfQojIE1ha2UgZmFrZSBMSU1PTiBPYmplY3QgdG8gUGFzcyB0byBOZXR3b3JrIEluZmVyZW5jZSBTdGVwcwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCm9yaWdpbmFsX2RhdGEgPC0gcmVhZC5jc3YoaGVyZSgiRGF0YSIsICJHTFZfU2ltRGF0YSIsICJEYXRhc2V0XzIiLCJHTFZfQ292X04yMC5jc3YiKSkKb3JpZ2luYWxfZGF0YSA8LSBvcmlnaW5hbF9kYXRhICU+JSBjb2x1bW5fdG9fcm93bmFtZXMoIlgiKQoKIyBMb29wIHRocm91Z2ggdGhlIHRpbWUgcG9pbnRzLCBmaWx0ZXIgZGF0YSwgYW5kIHJlbW92ZSBtZXRhZGF0YQpjb3VudHNfbGlzdCA8LSBsaXN0KCkKZm9yIChpIGluIDE6MTApIHsKICBmaWx0ZXJlZF9jb3VudHMgPC0gb3JpZ2luYWxfZGF0YSAlPiUgZmlsdGVyKFRpbWUgPT0gaSkKICBjb3VudHNfbGlzdFtbaV1dIDwtIHJvdW5kKGZpbHRlcmVkX2NvdW50c1ssIDY6NTVdKQp9CgojIEluaXRpYWxpemUgTF9vYmpfc2ltMgpMX29ial9zaW0yIDwtIExfb2JqMgoKIyBBc3NpZ24gdGhlIHByb2Nlc3NlZCBkYXRhIHRvIHRoZSBjb3JyZXNwb25kaW5nIHNsb3RzIGluIExfb2JqX3NpbTIKZm9yIChpIGluIDE6MTApIHsKICBMX29ial9zaW0yW1siQ29ycmVjdGVkX0NvdW50c19UaW1lIl1dW1twYXN0ZTAoIkNvcnJlY3RlZF9Db3VudHNfIiwgaSldXSA8LSBjb3VudHNfbGlzdFtbaV1dCn0KCgojIE5ldHdvcmsgSW5mZXJlbmNlCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBTZXQgc2VlZApzZXQuc2VlZCgxMjM0NSkKcHNlZWQgPC0gbGlzdChyZXAubnVtPTUwLCBzZWVkPTEwMDEwKQojIFNQSUVDLUVBU0kgcGVyIHRpbWUKTF9vYmpfc2ltMyA8LSBMSU1PTl9OZXRJbmZfVGltZShPYmogPSBMX29ial9zaW0yLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAiZ2xhc3NvIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsLmNyaXRlcmlvbiA9ICJic3RhcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhbWJkYS5taW4ucmF0aW8gPSAwLjAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHB1bHNhci5zZWxlY3Q9VFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHVsc2FyLnBhcmFtcz1wc2VlZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBubGFtYmRhID0gMjAwKQoKCiMgUHJpbnQgTmV0d29ya3MKTF9vYmpfc2ltNSA8LSBMSU1PTl9FZGdlc19OZXR3b3JrcyhMX29ial9zaW0zLCB0aHJlc2hvbGQgPSAwLjAwMDIsIHZlcnRleC5zaXplID0gMywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcnRleC5sYWJlbC5jZXggPSA4LCB2ZXJ0ZXgubGFiZWwuY29sb3IgPSAiYmxhY2siKQoKCgpgYGAKCgojIyMgMi4yLjQgLSBNZXJnZSBPdXRjb21lcwoKQ2FsY3VsYXRlIHRoZSBzaW1pbGFyIGVkZ2VzIGFjcm9zcyB0aGUgdGhyZWUgZGF0YSB0eXBlcwpgYGB7cn0KIyBMSU1PTiBEYXRhIC0gRXh0cmFjdCBFZGdlcwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyBJbml0aWFsaXplIGFuIGVtcHR5IGxpc3QgdG8gc3RvcmUgdGhlIGRhdGEgZnJhbWVzCkxfRWRnZXNfTGlzdCA8LSBsaXN0KCkKCiMgTG9vcCB0aHJvdWdoIHRoZSBlZGdlIHRhYmxlcyBhbmQgcHJvY2VzcyBlYWNoIG9uZQpmb3IgKGkgaW4gMToxMCkgewogIGVkZ2VfdGFibGUgPC0gYXMuZGF0YS5mcmFtZShMX29iajRbWyJFZGdlX1RhYmxlIl1dW1twYXN0ZTAoIkVkZ2VfVGFibGVfIiwgaSldXSkKICAjIENoZWNrIGlmIHRoZSBlZGdlIHRhYmxlIGlzIGVtcHR5CiAgaWYgKG5yb3coZWRnZV90YWJsZSkgPT0gMCkgewogICAgIyBDcmVhdGUgYSBkYXRhIGZyYW1lIHdpdGggTkEgdmFsdWVzCiAgICBlZGdlX3RhYmxlIDwtIGRhdGEuZnJhbWUoRWRnZV93ZWlnaHQgPSBOQSwgVGltZSA9IGkpCiAgfSBlbHNlIHsKICAgICMgZHBseXI6OnJlbmFtZSB0aGUgY29sdW1uIGFuZCBhZGQgdGhlIFRpbWUgY29sdW1uCiAgICBlZGdlX3RhYmxlIDwtIGVkZ2VfdGFibGUgJT4lIGRwbHlyOjpyZW5hbWUoIkxfRWRnZV93ZWlnaHQiID0gIkVkZ2Vfd2VpZ2h0IikKICAgIGVkZ2VfdGFibGUkVGltZSA8LSBpCiAgfQogICMgQXBwZW5kIHRoZSBwcm9jZXNzZWQgdGFibGUgdG8gdGhlIGxpc3QKICBMX0VkZ2VzX0xpc3RbW2ldXSA8LSBlZGdlX3RhYmxlCn0KIyBDb21iaW5lIGFsbCB0aGUgcHJvY2Vzc2VkIHRhYmxlcyBpbnRvIGEgc2luZ2xlIGRhdGEgZnJhbWUKTF9FZGdlcyA8LSBiaW5kX3Jvd3MoTF9FZGdlc19MaXN0KQoKCiMgT3JpZ2luYWwgRGF0YSArIENvdiAtIEV4dHJhY3QgZWRnZXMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCiMgSW5pdGlhbGl6ZSBhbiBlbXB0eSBsaXN0IHRvIHN0b3JlIHRoZSBkYXRhIGZyYW1lcwpDb3ZfRWRnZXNfTGlzdCA8LSBsaXN0KCkKCiMgTG9vcCB0aHJvdWdoIHRoZSBlZGdlIHRhYmxlcyBhbmQgcHJvY2VzcyBlYWNoIG9uZQpmb3IgKGkgaW4gMToxMCkgewogIGVkZ2VfdGFibGUgPC0gYXMuZGF0YS5mcmFtZShMX29ial9zaW01W1siRWRnZV9UYWJsZSJdXVtbcGFzdGUwKCJFZGdlX1RhYmxlXyIsIGkpXV0pCiAgIyBDaGVjayBpZiB0aGUgZWRnZSB0YWJsZSBpcyBlbXB0eQogIGlmIChucm93KGVkZ2VfdGFibGUpID09IDApIHsKICAgICMgQ3JlYXRlIGEgZGF0YSBmcmFtZSB3aXRoIE5BIHZhbHVlcwogICAgZWRnZV90YWJsZSA8LSBkYXRhLmZyYW1lKEVkZ2Vfd2VpZ2h0ID0gTkEsIFRpbWUgPSBpKQogIH0gZWxzZSB7CiAgICAjIGRwbHlyOjpyZW5hbWUgdGhlIGNvbHVtbiBhbmQgYWRkIHRoZSBUaW1lIGNvbHVtbgogICAgZWRnZV90YWJsZSA8LSBlZGdlX3RhYmxlICU+JSBkcGx5cjo6cmVuYW1lKCJDb3ZfRWRnZV93ZWlnaHQiPSAiRWRnZV93ZWlnaHQiKQogICAgZWRnZV90YWJsZSRUaW1lIDwtIGkKICB9CiAgIyBBcHBlbmQgdGhlIHByb2Nlc3NlZCB0YWJsZSB0byB0aGUgbGlzdAogIENvdl9FZGdlc19MaXN0W1tpXV0gPC0gZWRnZV90YWJsZQp9CiMgQ29tYmluZSBhbGwgdGhlIHByb2Nlc3NlZCB0YWJsZXMgaW50byBhIHNpbmdsZSBkYXRhIGZyYW1lCkNvdl9FZGdlcyA8LSBiaW5kX3Jvd3MoQ292X0VkZ2VzX0xpc3QpCgojIE9yaWdpbmFsIERhdGEgLSBFeHRyYWN0IEVkZ2VzCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgSW5pdGlhbGl6ZSBhbiBlbXB0eSBsaXN0IHRvIHN0b3JlIHRoZSBkYXRhIGZyYW1lcwpPX0VkZ2VzX0xpc3QgPC0gbGlzdCgpCgojIExvb3AgdGhyb3VnaCB0aGUgZWRnZSB0YWJsZXMgYW5kIHByb2Nlc3MgZWFjaCBvbmUKZm9yIChpIGluIDE6MTApIHsKICBlZGdlX3RhYmxlIDwtIGFzLmRhdGEuZnJhbWUoTF9vYmpfc2ltNFtbIkVkZ2VfVGFibGUiXV1bW3Bhc3RlMCgiRWRnZV9UYWJsZV8iLCBpKV1dKQogICMgQ2hlY2sgaWYgdGhlIGVkZ2UgdGFibGUgaXMgZW1wdHkKICBpZiAobnJvdyhlZGdlX3RhYmxlKSA9PSAwKSB7CiAgICAjIENyZWF0ZSBhIGRhdGEgZnJhbWUgd2l0aCBOQSB2YWx1ZXMKICAgIGVkZ2VfdGFibGUgPC0gZGF0YS5mcmFtZShFZGdlX3dlaWdodCA9IE5BLCBUaW1lID0gaSkKICB9IGVsc2UgewogICAgIyBkcGx5cjo6cmVuYW1lIHRoZSBjb2x1bW4gYW5kIGFkZCB0aGUgVGltZSBjb2x1bW4KICAgIGVkZ2VfdGFibGUgPC0gZWRnZV90YWJsZSAlPiUgZHBseXI6OnJlbmFtZSgiT19FZGdlX3dlaWdodCI9ICJFZGdlX3dlaWdodCIpCiAgICBlZGdlX3RhYmxlJFRpbWUgPC0gaQogIH0KICAjIEFwcGVuZCB0aGUgcHJvY2Vzc2VkIHRhYmxlIHRvIHRoZSBsaXN0CiAgT19FZGdlc19MaXN0W1tpXV0gPC0gZWRnZV90YWJsZQp9CiMgQ29tYmluZSBhbGwgdGhlIHByb2Nlc3NlZCB0YWJsZXMgaW50byBhIHNpbmdsZSBkYXRhIGZyYW1lCk9fRWRnZXMgPC0gYmluZF9yb3dzKE9fRWRnZXNfTGlzdCkKCgojIEJpbmQgZGF0YSB0b2dldGhlcgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpNZXJnZWRfVDEgPC0gbWVyZ2UoTF9FZGdlcywgT19FZGdlcywgYnkueSA9IGMoIlNvdXJjZSIsICJTaW5rIiwgIlRpbWUiKSwgYWxsPVRSVUUpCk1lcmdlZF9UMSA8LSBtZXJnZShNZXJnZWRfVDEsIENvdl9FZGdlcywgYnkueSA9IGMoIlNvdXJjZSIsICJTaW5rIiwgIlRpbWUiKSwgYWxsPVRSVUUpCgoKIyBDb21wYXJpc29uIEdyYXBoIERhdGEKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCiMgSWRlbnRpZnkgdHJ1ZSBlZGdlcwpNZXJnZWRfVDIwIDwtIE1lcmdlZF9UMSAlPiUKICBlZGdlX3JlY292ZXJ5KCkgCgojIEFkZCBTYW1wbGVTaXplIApNZXJnZWRfVDIwJFNhbXBsZVNpemUgPC0gMjAKYGBgCgoKCgojIyAyLjMgLSBTdWJqZWN0cyBOPTUwCgojIyMgMi4zLjEgLSBMSU1PTiBvbiBDb3ZhcmlhdGUgSW5mbGF0ZWQgRGF0YQpSdW4gdGhlIExJTU9OIGNvdmFyaWF0ZSBjb3JyZWN0aW9uIGFuZCBuZXR3b3JrIGluZmVyZW5jZSBzdGVwcyBvbiBDb3ZhcmlhdGUgaW5mbGF0ZWQgZGF0YS4gVGhlc2UgZWRnZXMgd2lsbCBiZSByZXF1aXJlZCBhcyBMX0VkZ2VzICAKCgpNYWtlIExJTU9OIE9iamVjdApgYGB7cn0KU2ltRGF0YSA8LSByZWFkLmNzdihoZXJlKCJEYXRhIiwgIkdMVl9TaW1EYXRhIiwgIkRhdGFzZXRfMiIsIkdMVl9Db3ZfTjUwLmNzdiIpKQpTaW1EYXRhIDwtIGNvbHVtbl90b19yb3duYW1lcyhTaW1EYXRhLCAiWCIpCgojIG1ldGFkYXRhCm1ldGFfZGF0YSA8LSBTaW1EYXRhWywxOjVdCm1ldGFfZGF0YSRUaW1lIDwtIGFzLm51bWVyaWMobWV0YV9kYXRhJFRpbWUpCm1ldGFfZGF0YSRCTUkgPC0gYXMubnVtZXJpYyhtZXRhX2RhdGEkQk1JKQptZXRhX2RhdGEkQWdlIDwtIGFzLm51bWVyaWMobWV0YV9kYXRhJEFnZSkKCiMgT3B0aW9uIHRvIHNob3J0ZW4gdG8gcnVuIGZhc3RlcgpsaW1vbl9tZXRhIDwtIG1ldGFfZGF0YSAjJT4lIGZpbHRlcihUaW1lICVpbiUgYygxLDIsMyw0LDUpKQoKIyBDb3VudCBkYXRhCmxpbW9uX2NvdW50cyA8LSBhcy5tYXRyaXgoU2ltRGF0YVssNjo1NV0pCgojIHNvcnQgcm93cwpsaW1vbl9jb3VudHMgPC0gbGltb25fY291bnRzW3Jvd25hbWVzKGxpbW9uX21ldGEpLF0KCiAgCkxfb2JqIDwtIExJTU9OX09iaihDb3VudHMgPSBsaW1vbl9jb3VudHMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICBTYW1wbGVEYXRhID0gbGltb25fbWV0YSkKYGBgCgoKRGlzdHJpYnV0aW9uIEZpdHRpbmcgYW5kIGNoZWNrCmBgYHtyfQojIFNldCBzZWVkCnNldC5zZWVkKDEyMzQ1KQojIEZpdCB0aGUgZGlzdHJpYnV0aW9uL3JlbW92ZSBjb3ZhcmlhdGVzCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpMX29iajIgPC0gTElNT05fRGlzdHJGaXQoT2JqID0gTF9vYmosIAogICAgICAgICAgICAgICAgICAgICAgICAgICBUaW1lID0gIlRpbWUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgU3ViamVjdCA9ICJJRCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICBDb3ZhcmlhdGVzID0gYygiU2V4IiwgIkJNSSIsICJBZ2UiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZWwgPSAiU2V4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlzdHJpYnV0aW9uID0gIkxNTSIpCmBgYAoKCk5ldHdvcmtzIG9mIExJTU9OIERhdGEKYGBge3J9CiMgU2V0IHNlZWQKc2V0LnNlZWQoMTIzNDUpCnBzZWVkIDwtIGxpc3QocmVwLm51bT01MCwgc2VlZD0xMDAxMCkKCiMgU1BJRUMtRUFTSSBwZXIgdGltZQpMX29iajMgPC0gTElNT05fTmV0SW5mX1RpbWUoT2JqID0gTF9vYmoyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAiZ2xhc3NvIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsLmNyaXRlcmlvbiA9ICJic3RhcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhbWJkYS5taW4ucmF0aW8gPSAwLjAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHB1bHNhci5zZWxlY3Q9VFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHVsc2FyLnBhcmFtcz1wc2VlZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBubGFtYmRhID0gMjAwKQoKCiMgUHJpbnQgTmV0d29ya3MKTF9vYmo0IDwtIExJTU9OX0VkZ2VzX05ldHdvcmtzKExfb2JqMywgdGhyZXNob2xkID0gMC4wMDAyLCB2ZXJ0ZXguc2l6ZSA9IDMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJ0ZXgubGFiZWwuY2V4ID0gOCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcnRleC5sYWJlbC5jb2xvciA9ICJibGFjayIpCmBgYAoKIyMjIDIuMy4yIC0gVHJ1ZSBOZXR3b3JrcyAtIG5vIENvdmFyaWF0ZXMgIAoKU1BJRUMtRUFTSSBOZXR3b3JrcyBvZiB0aGUgUmF3IERhdGEgd2l0aG91dCBjb3ZhcmlhdGVzLiBUaGVzZSB3aWxsIGJlIGxhYmVsZWQgYXMgT19FZGdlcyAoZm9yIG9yaWdpbmFsIGVkZ2VzKS4gV2UgcmVhZCBpbiB0aGUgZGF0YSBhbmQgdGhlbiBhc3NpZ24gaXQgdG8gYSBkb3duc3RyZWFtIExJTU9uIG9iamVjdCB0byBydW4gU1BJRUMtRUFTSSBhY3Jvc3MgYWxsIHRoZSB0aW1lIHBvaW50cyBidXQgYnlwYXNzaW5nIHRoZSBuZXR3b3JrIGluZmVyZW5jZSBzdGVwCmBgYHtyfQojIE1ha2UgZmFrZSBMSU1PTiBPYmplY3QgdG8gUGFzcyB0byBOZXR3b3JrIEluZmVyZW5jZSBTdGVwcwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCm9yaWdpbmFsX2RhdGEgPC0gcmVhZC5jc3YoaGVyZSgiRGF0YSIsICJHTFZfU2ltRGF0YSIsICJEYXRhc2V0XzIiLCJHTFZfTjUwLmNzdiIpKQpvcmlnaW5hbF9kYXRhIDwtIG9yaWdpbmFsX2RhdGEgJT4lIGNvbHVtbl90b19yb3duYW1lcygiWCIpCgojIExvb3AgdGhyb3VnaCB0aGUgdGltZSBwb2ludHMsIGZpbHRlciBkYXRhLCBhbmQgcmVtb3ZlIG1ldGFkYXRhCmNvdW50c19saXN0IDwtIGxpc3QoKQpmb3IgKGkgaW4gMToxMCkgewogIGZpbHRlcmVkX2NvdW50cyA8LSBvcmlnaW5hbF9kYXRhICU+JSBmaWx0ZXIoVGltZSA9PSBpKQogIGNvdW50c19saXN0W1tpXV0gPC0gcm91bmQoZmlsdGVyZWRfY291bnRzWywgNjo1NV0pCn0KCiMgSW5pdGlhbGl6ZSBMX29ial9zaW0yCkxfb2JqX3NpbTIgPC0gTF9vYmoyCgojIEFzc2lnbiB0aGUgcHJvY2Vzc2VkIGRhdGEgdG8gdGhlIGNvcnJlc3BvbmRpbmcgc2xvdHMgaW4gTF9vYmpfc2ltMgpmb3IgKGkgaW4gMToxMCkgewogIExfb2JqX3NpbTJbWyJDb3JyZWN0ZWRfQ291bnRzX1RpbWUiXV1bW3Bhc3RlMCgiQ29ycmVjdGVkX0NvdW50c18iLCBpKV1dIDwtIGNvdW50c19saXN0W1tpXV0KfQoKCiMgTmV0d29yayBJbmZlcmVuY2UKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFNldCBzZWVkCnNldC5zZWVkKDEyMzQ1KQpwc2VlZCA8LSBsaXN0KHJlcC5udW09NTAsIHNlZWQ9MTAwMTApCiMgU1BJRUMtRUFTSSBwZXIgdGltZQpMX29ial9zaW0zIDwtIExJTU9OX05ldEluZl9UaW1lKE9iaiA9IExfb2JqX3NpbTIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJnbGFzc28iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWwuY3JpdGVyaW9uID0gImJzdGFycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFtYmRhLm1pbi5yYXRpbyA9IDAuMDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHVsc2FyLnNlbGVjdD1UUlVFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdWxzYXIucGFyYW1zPXBzZWVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5sYW1iZGEgPSAyMDApCgoKIyBQcmludCBOZXR3b3JrcwpMX29ial9zaW00IDwtIExJTU9OX0VkZ2VzX05ldHdvcmtzKExfb2JqX3NpbTMsIHRocmVzaG9sZCA9IDAuMDAwMiwgdmVydGV4LnNpemUgPSAzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVydGV4LmxhYmVsLmNleCA9IDgsIHZlcnRleC5sYWJlbC5jb2xvciA9ICJibGFjayIpCgoKCmBgYAoKCgoKIyMjIDIuMy4zIC0gU1BJRUMtRUFTSSBvbiBDb3ZhcmlhdGUgRGF0YSAgClNpbWlsYXIgdG8gcHJvY2VzcyBmb3Igb3JpZ2luYWwgbmV0d29ya3MsIGV4Y2VwdCBub3cgd2Ugd2lsbCBydW4gaXQgb24gY292YXJpYXRlIGNvbmZsYXRlZCBkYXRhLiBUaGVzZSB3aWxsIGJlIGxhYmVsZWQgYXMgQ292X0VkZ2VzCmBgYHtyfQojIE1ha2UgZmFrZSBMSU1PTiBPYmplY3QgdG8gUGFzcyB0byBOZXR3b3JrIEluZmVyZW5jZSBTdGVwcwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCm9yaWdpbmFsX2RhdGEgPC0gcmVhZC5jc3YoaGVyZSgiRGF0YSIsICJHTFZfU2ltRGF0YSIsICJEYXRhc2V0XzIiLCJHTFZfQ292X041MC5jc3YiKSkKb3JpZ2luYWxfZGF0YSA8LSBvcmlnaW5hbF9kYXRhICU+JSBjb2x1bW5fdG9fcm93bmFtZXMoIlgiKQoKIyBMb29wIHRocm91Z2ggdGhlIHRpbWUgcG9pbnRzLCBmaWx0ZXIgZGF0YSwgYW5kIHJlbW92ZSBtZXRhZGF0YQpjb3VudHNfbGlzdCA8LSBsaXN0KCkKZm9yIChpIGluIDE6MTApIHsKICBmaWx0ZXJlZF9jb3VudHMgPC0gb3JpZ2luYWxfZGF0YSAlPiUgZmlsdGVyKFRpbWUgPT0gaSkKICBjb3VudHNfbGlzdFtbaV1dIDwtIHJvdW5kKGZpbHRlcmVkX2NvdW50c1ssIDY6NTVdKQp9CgojIEluaXRpYWxpemUgTF9vYmpfc2ltMgpMX29ial9zaW0yIDwtIExfb2JqMgoKIyBBc3NpZ24gdGhlIHByb2Nlc3NlZCBkYXRhIHRvIHRoZSBjb3JyZXNwb25kaW5nIHNsb3RzIGluIExfb2JqX3NpbTIKZm9yIChpIGluIDE6MTApIHsKICBMX29ial9zaW0yW1siQ29ycmVjdGVkX0NvdW50c19UaW1lIl1dW1twYXN0ZTAoIkNvcnJlY3RlZF9Db3VudHNfIiwgaSldXSA8LSBjb3VudHNfbGlzdFtbaV1dCn0KCgojIE5ldHdvcmsgSW5mZXJlbmNlCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBTZXQgc2VlZApzZXQuc2VlZCgxMjM0NSkKcHNlZWQgPC0gbGlzdChyZXAubnVtPTUwLCBzZWVkPTEwMDEwKQojIFNQSUVDLUVBU0kgcGVyIHRpbWUKTF9vYmpfc2ltMyA8LSBMSU1PTl9OZXRJbmZfVGltZShPYmogPSBMX29ial9zaW0yLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAiZ2xhc3NvIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsLmNyaXRlcmlvbiA9ICJic3RhcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhbWJkYS5taW4ucmF0aW8gPSAwLjAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHB1bHNhci5zZWxlY3Q9VFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHVsc2FyLnBhcmFtcz1wc2VlZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBubGFtYmRhID0gMjAwKQoKCiMgUHJpbnQgTmV0d29ya3MKTF9vYmpfc2ltNSA8LSBMSU1PTl9FZGdlc19OZXR3b3JrcyhMX29ial9zaW0zLCB0aHJlc2hvbGQgPSAwLjAwMDIsIHZlcnRleC5zaXplID0gMywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcnRleC5sYWJlbC5jZXggPSA4LCB2ZXJ0ZXgubGFiZWwuY29sb3IgPSAiYmxhY2siKQoKCgpgYGAKCgojIyMgMi4zLjQgLSBNZXJnZSBPdXRjb21lcwoKQ2FsY3VsYXRlIHRoZSBzaW1pbGFyIGVkZ2VzIGFjcm9zcyB0aGUgdGhyZWUgZGF0YSB0eXBlcwpgYGB7cn0KIyBMSU1PTiBEYXRhIC0gRXh0cmFjdCBFZGdlcwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyBJbml0aWFsaXplIGFuIGVtcHR5IGxpc3QgdG8gc3RvcmUgdGhlIGRhdGEgZnJhbWVzCkxfRWRnZXNfTGlzdCA8LSBsaXN0KCkKCiMgTG9vcCB0aHJvdWdoIHRoZSBlZGdlIHRhYmxlcyBhbmQgcHJvY2VzcyBlYWNoIG9uZQpmb3IgKGkgaW4gMToxMCkgewogIGVkZ2VfdGFibGUgPC0gYXMuZGF0YS5mcmFtZShMX29iajRbWyJFZGdlX1RhYmxlIl1dW1twYXN0ZTAoIkVkZ2VfVGFibGVfIiwgaSldXSkKICAjIENoZWNrIGlmIHRoZSBlZGdlIHRhYmxlIGlzIGVtcHR5CiAgaWYgKG5yb3coZWRnZV90YWJsZSkgPT0gMCkgewogICAgIyBDcmVhdGUgYSBkYXRhIGZyYW1lIHdpdGggTkEgdmFsdWVzCiAgICBlZGdlX3RhYmxlIDwtIGRhdGEuZnJhbWUoRWRnZV93ZWlnaHQgPSBOQSwgVGltZSA9IGkpCiAgfSBlbHNlIHsKICAgICMgZHBseXI6OnJlbmFtZSB0aGUgY29sdW1uIGFuZCBhZGQgdGhlIFRpbWUgY29sdW1uCiAgICBlZGdlX3RhYmxlIDwtIGVkZ2VfdGFibGUgJT4lIGRwbHlyOjpyZW5hbWUoIkxfRWRnZV93ZWlnaHQiPSAiRWRnZV93ZWlnaHQiKQogICAgZWRnZV90YWJsZSRUaW1lIDwtIGkKICB9CiAgIyBBcHBlbmQgdGhlIHByb2Nlc3NlZCB0YWJsZSB0byB0aGUgbGlzdAogIExfRWRnZXNfTGlzdFtbaV1dIDwtIGVkZ2VfdGFibGUKfQojIENvbWJpbmUgYWxsIHRoZSBwcm9jZXNzZWQgdGFibGVzIGludG8gYSBzaW5nbGUgZGF0YSBmcmFtZQpMX0VkZ2VzIDwtIGJpbmRfcm93cyhMX0VkZ2VzX0xpc3QpCgoKIyBPcmlnaW5hbCBEYXRhICsgQ292IC0gRXh0cmFjdCBlZGdlcwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyBJbml0aWFsaXplIGFuIGVtcHR5IGxpc3QgdG8gc3RvcmUgdGhlIGRhdGEgZnJhbWVzCkNvdl9FZGdlc19MaXN0IDwtIGxpc3QoKQoKIyBMb29wIHRocm91Z2ggdGhlIGVkZ2UgdGFibGVzIGFuZCBwcm9jZXNzIGVhY2ggb25lCmZvciAoaSBpbiAxOjEwKSB7CiAgZWRnZV90YWJsZSA8LSBhcy5kYXRhLmZyYW1lKExfb2JqX3NpbTVbWyJFZGdlX1RhYmxlIl1dW1twYXN0ZTAoIkVkZ2VfVGFibGVfIiwgaSldXSkKICAjIENoZWNrIGlmIHRoZSBlZGdlIHRhYmxlIGlzIGVtcHR5CiAgaWYgKG5yb3coZWRnZV90YWJsZSkgPT0gMCkgewogICAgIyBDcmVhdGUgYSBkYXRhIGZyYW1lIHdpdGggTkEgdmFsdWVzCiAgICBlZGdlX3RhYmxlIDwtIGRhdGEuZnJhbWUoRWRnZV93ZWlnaHQgPSBOQSwgVGltZSA9IGkpCiAgfSBlbHNlIHsKICAgICMgZHBseXI6OnJlbmFtZSB0aGUgY29sdW1uIGFuZCBhZGQgdGhlIFRpbWUgY29sdW1uCiAgICBlZGdlX3RhYmxlIDwtIGVkZ2VfdGFibGUgJT4lIGRwbHlyOjpyZW5hbWUoIkNvdl9FZGdlX3dlaWdodCI9ICJFZGdlX3dlaWdodCIpCiAgICBlZGdlX3RhYmxlJFRpbWUgPC0gaQogIH0KICAjIEFwcGVuZCB0aGUgcHJvY2Vzc2VkIHRhYmxlIHRvIHRoZSBsaXN0CiAgQ292X0VkZ2VzX0xpc3RbW2ldXSA8LSBlZGdlX3RhYmxlCn0KIyBDb21iaW5lIGFsbCB0aGUgcHJvY2Vzc2VkIHRhYmxlcyBpbnRvIGEgc2luZ2xlIGRhdGEgZnJhbWUKQ292X0VkZ2VzIDwtIGJpbmRfcm93cyhDb3ZfRWRnZXNfTGlzdCkKCiMgT3JpZ2luYWwgRGF0YSAtIEV4dHJhY3QgRWRnZXMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBJbml0aWFsaXplIGFuIGVtcHR5IGxpc3QgdG8gc3RvcmUgdGhlIGRhdGEgZnJhbWVzCk9fRWRnZXNfTGlzdCA8LSBsaXN0KCkKCiMgTG9vcCB0aHJvdWdoIHRoZSBlZGdlIHRhYmxlcyBhbmQgcHJvY2VzcyBlYWNoIG9uZQpmb3IgKGkgaW4gMToxMCkgewogIGVkZ2VfdGFibGUgPC0gYXMuZGF0YS5mcmFtZShMX29ial9zaW00W1siRWRnZV9UYWJsZSJdXVtbcGFzdGUwKCJFZGdlX1RhYmxlXyIsIGkpXV0pCiAgIyBDaGVjayBpZiB0aGUgZWRnZSB0YWJsZSBpcyBlbXB0eQogIGlmIChucm93KGVkZ2VfdGFibGUpID09IDApIHsKICAgICMgQ3JlYXRlIGEgZGF0YSBmcmFtZSB3aXRoIE5BIHZhbHVlcwogICAgZWRnZV90YWJsZSA8LSBkYXRhLmZyYW1lKEVkZ2Vfd2VpZ2h0ID0gTkEsIFRpbWUgPSBpKQogIH0gZWxzZSB7CiAgICAjIGRwbHlyOjpyZW5hbWUgdGhlIGNvbHVtbiBhbmQgYWRkIHRoZSBUaW1lIGNvbHVtbgogICAgZWRnZV90YWJsZSA8LSBlZGdlX3RhYmxlICU+JSBkcGx5cjo6cmVuYW1lKCJPX0VkZ2Vfd2VpZ2h0Ij0gIkVkZ2Vfd2VpZ2h0IikKICAgIGVkZ2VfdGFibGUkVGltZSA8LSBpCiAgfQogICMgQXBwZW5kIHRoZSBwcm9jZXNzZWQgdGFibGUgdG8gdGhlIGxpc3QKICBPX0VkZ2VzX0xpc3RbW2ldXSA8LSBlZGdlX3RhYmxlCn0KIyBDb21iaW5lIGFsbCB0aGUgcHJvY2Vzc2VkIHRhYmxlcyBpbnRvIGEgc2luZ2xlIGRhdGEgZnJhbWUKT19FZGdlcyA8LSBiaW5kX3Jvd3MoT19FZGdlc19MaXN0KQoKIyBCaW5kIGRhdGEgdG9nZXRoZXIKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKTWVyZ2VkX1QxIDwtIG1lcmdlKExfRWRnZXMsIE9fRWRnZXMsIGJ5LnkgPSBjKCJTb3VyY2UiLCAiU2luayIsICJUaW1lIiksIGFsbD1UUlVFKQpNZXJnZWRfVDEgPC0gbWVyZ2UoTWVyZ2VkX1QxLCBDb3ZfRWRnZXMsIGJ5LnkgPSBjKCJTb3VyY2UiLCAiU2luayIsICJUaW1lIiksIGFsbD1UUlVFKQoKCiMgQ29tcGFyaXNvbiBHcmFwaCBEYXRhCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIElkZW50aWZ5IHRydWUgZWRnZXMKTWVyZ2VkX1Q1MCA8LSBNZXJnZWRfVDEgJT4lCiAgZWRnZV9yZWNvdmVyeSgpIAoKIyBBZGQgU2FtcGxlU2l6ZSAKTWVyZ2VkX1Q1MCRTYW1wbGVTaXplIDwtIDUwCmBgYAoKCgojIyAyLjQgLSBTdWJqZWN0cyBOPTc1CgojIyMgMi40LjEgLSBMSU1PTiBvbiBDb3ZhcmlhdGUgSW5mbGF0ZWQgRGF0YQpSdW4gdGhlIExJTU9OIGNvdmFyaWF0ZSBjb3JyZWN0aW9uIGFuZCBuZXR3b3JrIGluZmVyZW5jZSBzdGVwcyBvbiBDb3ZhcmlhdGUgaW5mbGF0ZWQgZGF0YS4gVGhlc2UgZWRnZXMgd2lsbCBiZSByZXF1aXJlZCBhcyBMX0VkZ2VzICAKCgpNYWtlIExJTU9OIE9iamVjdApgYGB7cn0KU2ltRGF0YSA8LSByZWFkLmNzdihoZXJlKCJEYXRhIiwgIkdMVl9TaW1EYXRhIiwgIkRhdGFzZXRfMiIsIkdMVl9Db3ZfTjc1LmNzdiIpKQpTaW1EYXRhIDwtIGNvbHVtbl90b19yb3duYW1lcyhTaW1EYXRhLCAiWCIpCgojIG1ldGFkYXRhCm1ldGFfZGF0YSA8LSBTaW1EYXRhWywxOjVdCm1ldGFfZGF0YSRUaW1lIDwtIGFzLm51bWVyaWMobWV0YV9kYXRhJFRpbWUpCm1ldGFfZGF0YSRCTUkgPC0gYXMubnVtZXJpYyhtZXRhX2RhdGEkQk1JKQptZXRhX2RhdGEkQWdlIDwtIGFzLm51bWVyaWMobWV0YV9kYXRhJEFnZSkKCiMgT3B0aW9uIHRvIHNob3J0ZW4gdG8gcnVuIGZhc3RlcgpsaW1vbl9tZXRhIDwtIG1ldGFfZGF0YSAjJT4lIGZpbHRlcihUaW1lICVpbiUgYygxLDIsMyw0LDUpKQoKIyBDb3VudCBkYXRhCmxpbW9uX2NvdW50cyA8LSBhcy5tYXRyaXgoU2ltRGF0YVssNjo1NV0pCgojIHNvcnQgcm93cwpsaW1vbl9jb3VudHMgPC0gbGltb25fY291bnRzW3Jvd25hbWVzKGxpbW9uX21ldGEpLF0KCiAgCkxfb2JqIDwtIExJTU9OX09iaihDb3VudHMgPSBsaW1vbl9jb3VudHMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICBTYW1wbGVEYXRhID0gbGltb25fbWV0YSkKYGBgCgoKRGlzdHJpYnV0aW9uIEZpdHRpbmcgYW5kIGNoZWNrCmBgYHtyfQojIFNldCBzZWVkCnNldC5zZWVkKDEyMzQ1KQojIEZpdCB0aGUgZGlzdHJpYnV0aW9uL3JlbW92ZSBjb3ZhcmlhdGVzCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpMX29iajIgPC0gTElNT05fRGlzdHJGaXQoT2JqID0gTF9vYmosIAogICAgICAgICAgICAgICAgICAgICAgICAgICBUaW1lID0gIlRpbWUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgU3ViamVjdCA9ICJJRCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICBDb3ZhcmlhdGVzID0gYygiU2V4IiwgIkJNSSIsICJBZ2UiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZWwgPSAiU2V4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlzdHJpYnV0aW9uID0gIkxNTSIpCmBgYAoKCk5ldHdvcmtzIG9mIExJTU9OIERhdGEKYGBge3J9CiMgU2V0IHNlZWQKc2V0LnNlZWQoMTIzNDUpCnBzZWVkIDwtIGxpc3QocmVwLm51bT01MCwgc2VlZD0xMDAxMCkKCiMgU1BJRUMtRUFTSSBwZXIgdGltZQpMX29iajMgPC0gTElNT05fTmV0SW5mX1RpbWUoT2JqID0gTF9vYmoyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAiZ2xhc3NvIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsLmNyaXRlcmlvbiA9ICJic3RhcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhbWJkYS5taW4ucmF0aW8gPSAwLjAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHB1bHNhci5zZWxlY3Q9VFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHVsc2FyLnBhcmFtcz1wc2VlZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBubGFtYmRhID0gMjAwKQoKCiMgUHJpbnQgTmV0d29ya3MKTF9vYmo0IDwtIExJTU9OX0VkZ2VzX05ldHdvcmtzKExfb2JqMywgdGhyZXNob2xkID0gMC4wMDAyLCB2ZXJ0ZXguc2l6ZSA9IDMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJ0ZXgubGFiZWwuY2V4ID0gOCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcnRleC5sYWJlbC5jb2xvciA9ICJibGFjayIpCmBgYAoKIyMjIDIuNC4yIC0gVHJ1ZSBOZXR3b3JrcyAtIG5vIENvdmFyaWF0ZXMgIAoKU1BJRUMtRUFTSSBOZXR3b3JrcyBvZiB0aGUgUmF3IERhdGEgd2l0aG91dCBjb3ZhcmlhdGVzLiBUaGVzZSB3aWxsIGJlIGxhYmVsZWQgYXMgT19FZGdlcyAoZm9yIG9yaWdpbmFsIGVkZ2VzKS4gV2UgcmVhZCBpbiB0aGUgZGF0YSBhbmQgdGhlbiBhc3NpZ24gaXQgdG8gYSBkb3duc3RyZWFtIExJTU9uIG9iamVjdCB0byBydW4gU1BJRUMtRUFTSSBhY3Jvc3MgYWxsIHRoZSB0aW1lcG9pbnRzIGJ1dCBieXBhc3NpbmcgdGhlIG5ldHdvcmsgaW5mZXJlbmNlIHN0ZXAKYGBge3J9CiMgTWFrZSBmYWtlIExJTU9OIE9iamVjdCB0byBQYXNzIHRvIE5ldHdvcmsgSW5mZXJlbmNlIFN0ZXBzCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKb3JpZ2luYWxfZGF0YSA8LSByZWFkLmNzdihoZXJlKCJEYXRhIiwgIkdMVl9TaW1EYXRhIiwgIkRhdGFzZXRfMiIsIkdMVl9ONzUuY3N2IikpCm9yaWdpbmFsX2RhdGEgPC0gb3JpZ2luYWxfZGF0YSAlPiUgY29sdW1uX3RvX3Jvd25hbWVzKCJYIikKCiMgTG9vcCB0aHJvdWdoIHRoZSB0aW1lIHBvaW50cywgZmlsdGVyIGRhdGEsIGFuZCByZW1vdmUgbWV0YWRhdGEKY291bnRzX2xpc3QgPC0gbGlzdCgpCmZvciAoaSBpbiAxOjEwKSB7CiAgZmlsdGVyZWRfY291bnRzIDwtIG9yaWdpbmFsX2RhdGEgJT4lIGZpbHRlcihUaW1lID09IGkpCiAgY291bnRzX2xpc3RbW2ldXSA8LSByb3VuZChmaWx0ZXJlZF9jb3VudHNbLCA2OjU1XSkKfQoKIyBJbml0aWFsaXplIExfb2JqX3NpbTIKTF9vYmpfc2ltMiA8LSBMX29iajIKCiMgQXNzaWduIHRoZSBwcm9jZXNzZWQgZGF0YSB0byB0aGUgY29ycmVzcG9uZGluZyBzbG90cyBpbiBMX29ial9zaW0yCmZvciAoaSBpbiAxOjEwKSB7CiAgTF9vYmpfc2ltMltbIkNvcnJlY3RlZF9Db3VudHNfVGltZSJdXVtbcGFzdGUwKCJDb3JyZWN0ZWRfQ291bnRzXyIsIGkpXV0gPC0gY291bnRzX2xpc3RbW2ldXQp9CgoKIyBOZXR3b3JrIEluZmVyZW5jZQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU2V0IHNlZWQKc2V0LnNlZWQoMTIzNDUpCnBzZWVkIDwtIGxpc3QocmVwLm51bT01MCwgc2VlZD0xMDAxMCkKIyBTUElFQy1FQVNJIHBlciB0aW1lCkxfb2JqX3NpbTMgPC0gTElNT05fTmV0SW5mX1RpbWUoT2JqID0gTF9vYmpfc2ltMiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gImdsYXNzbyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbC5jcml0ZXJpb24gPSAiYnN0YXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYW1iZGEubWluLnJhdGlvID0gMC4wMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdWxzYXIuc2VsZWN0PVRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHB1bHNhci5wYXJhbXM9cHNlZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmxhbWJkYSA9IDIwMCkKCgojIFByaW50IE5ldHdvcmtzCkxfb2JqX3NpbTQgPC0gTElNT05fRWRnZXNfTmV0d29ya3MoTF9vYmpfc2ltMywgdGhyZXNob2xkID0gMC4wMDAyLCB2ZXJ0ZXguc2l6ZSA9IDMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJ0ZXgubGFiZWwuY2V4ID0gOCwgdmVydGV4LmxhYmVsLmNvbG9yID0gImJsYWNrIikKCgoKYGBgCgoKCgojIyMgMi40LjMgLSBTUElFQy1FQVNJIG9uIENvdmFyaWF0ZSBEYXRhICAKU2ltaWxhciB0byBwcm9jZXNzIGZvciBvcmlnaW5hbCBuZXR3b3JrcywgZXhjZXB0IG5vdyB3ZSB3aWxsIHJ1biBpdCBvbiBjb3ZhcmlhdGUgY29uZmxhdGVkIGRhdGEuIFRoZXNlIHdpbGwgYmUgbGFiZWxlZCBhcyBDb3ZfRWRnZXMKYGBge3J9CiMgTWFrZSBmYWtlIExJTU9OIE9iamVjdCB0byBQYXNzIHRvIE5ldHdvcmsgSW5mZXJlbmNlIFN0ZXBzCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKb3JpZ2luYWxfZGF0YSA8LSByZWFkLmNzdihoZXJlKCJEYXRhIiwgIkdMVl9TaW1EYXRhIiwgIkRhdGFzZXRfMiIsIkdMVl9Db3ZfTjc1LmNzdiIpKQpvcmlnaW5hbF9kYXRhIDwtIG9yaWdpbmFsX2RhdGEgJT4lIGNvbHVtbl90b19yb3duYW1lcygiWCIpCgojIExvb3AgdGhyb3VnaCB0aGUgdGltZSBwb2ludHMsIGZpbHRlciBkYXRhLCBhbmQgcmVtb3ZlIG1ldGFkYXRhCmNvdW50c19saXN0IDwtIGxpc3QoKQpmb3IgKGkgaW4gMToxMCkgewogIGZpbHRlcmVkX2NvdW50cyA8LSBvcmlnaW5hbF9kYXRhICU+JSBmaWx0ZXIoVGltZSA9PSBpKQogIGNvdW50c19saXN0W1tpXV0gPC0gcm91bmQoZmlsdGVyZWRfY291bnRzWywgNjo1NV0pCn0KCiMgSW5pdGlhbGl6ZSBMX29ial9zaW0yCkxfb2JqX3NpbTIgPC0gTF9vYmoyCgojIEFzc2lnbiB0aGUgcHJvY2Vzc2VkIGRhdGEgdG8gdGhlIGNvcnJlc3BvbmRpbmcgc2xvdHMgaW4gTF9vYmpfc2ltMgpmb3IgKGkgaW4gMToxMCkgewogIExfb2JqX3NpbTJbWyJDb3JyZWN0ZWRfQ291bnRzX1RpbWUiXV1bW3Bhc3RlMCgiQ29ycmVjdGVkX0NvdW50c18iLCBpKV1dIDwtIGNvdW50c19saXN0W1tpXV0KfQoKCiMgTmV0d29yayBJbmZlcmVuY2UKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFNldCBzZWVkCnNldC5zZWVkKDEyMzQ1KQpwc2VlZCA8LSBsaXN0KHJlcC5udW09NTAsIHNlZWQ9MTAwMTApCiMgU1BJRUMtRUFTSSBwZXIgdGltZQpMX29ial9zaW0zIDwtIExJTU9OX05ldEluZl9UaW1lKE9iaiA9IExfb2JqX3NpbTIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJnbGFzc28iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWwuY3JpdGVyaW9uID0gImJzdGFycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFtYmRhLm1pbi5yYXRpbyA9IDAuMDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHVsc2FyLnNlbGVjdD1UUlVFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdWxzYXIucGFyYW1zPXBzZWVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5sYW1iZGEgPSAyMDApCgoKIyBQcmludCBOZXR3b3JrcwpMX29ial9zaW01IDwtIExJTU9OX0VkZ2VzX05ldHdvcmtzKExfb2JqX3NpbTMsIHRocmVzaG9sZCA9IDAuMDAwMiwgdmVydGV4LnNpemUgPSAzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVydGV4LmxhYmVsLmNleCA9IDgsIHZlcnRleC5sYWJlbC5jb2xvciA9ICJibGFjayIpCgoKCmBgYAoKCiMjIyAyLjQuNCAtIE1lcmdlIE91dGNvbWVzCkNhbGN1bGF0ZSB0aGUgc2ltaWxhciBlZGdlcyBhY3Jvc3MgdGhlIHRocmVlIGRhdGEgdHlwZXMKYGBge3J9CiMgTElNT04gRGF0YSAtIEV4dHJhY3QgRWRnZXMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCiMgSW5pdGlhbGl6ZSBhbiBlbXB0eSBsaXN0IHRvIHN0b3JlIHRoZSBkYXRhIGZyYW1lcwpMX0VkZ2VzX0xpc3QgPC0gbGlzdCgpCgojIExvb3AgdGhyb3VnaCB0aGUgZWRnZSB0YWJsZXMgYW5kIHByb2Nlc3MgZWFjaCBvbmUKZm9yIChpIGluIDE6MTApIHsKICBlZGdlX3RhYmxlIDwtIGFzLmRhdGEuZnJhbWUoTF9vYmo0W1siRWRnZV9UYWJsZSJdXVtbcGFzdGUwKCJFZGdlX1RhYmxlXyIsIGkpXV0pCiAgIyBDaGVjayBpZiB0aGUgZWRnZSB0YWJsZSBpcyBlbXB0eQogIGlmIChucm93KGVkZ2VfdGFibGUpID09IDApIHsKICAgICMgQ3JlYXRlIGEgZGF0YSBmcmFtZSB3aXRoIE5BIHZhbHVlcwogICAgZWRnZV90YWJsZSA8LSBkYXRhLmZyYW1lKEVkZ2Vfd2VpZ2h0ID0gTkEsIFRpbWUgPSBpKQogIH0gZWxzZSB7CiAgICAjIGRwbHlyOjpyZW5hbWUgdGhlIGNvbHVtbiBhbmQgYWRkIHRoZSBUaW1lIGNvbHVtbgogICAgZWRnZV90YWJsZSA8LSBlZGdlX3RhYmxlICU+JSBkcGx5cjo6cmVuYW1lKCJMX0VkZ2Vfd2VpZ2h0Ij0gIkVkZ2Vfd2VpZ2h0IikKICAgIGVkZ2VfdGFibGUkVGltZSA8LSBpCiAgfQogICMgQXBwZW5kIHRoZSBwcm9jZXNzZWQgdGFibGUgdG8gdGhlIGxpc3QKICBMX0VkZ2VzX0xpc3RbW2ldXSA8LSBlZGdlX3RhYmxlCn0KIyBDb21iaW5lIGFsbCB0aGUgcHJvY2Vzc2VkIHRhYmxlcyBpbnRvIGEgc2luZ2xlIGRhdGEgZnJhbWUKTF9FZGdlcyA8LSBiaW5kX3Jvd3MoTF9FZGdlc19MaXN0KQoKIyBPcmlnaW5hbCBEYXRhICsgQ292IC0gRXh0cmFjdCBlZGdlcwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyBJbml0aWFsaXplIGFuIGVtcHR5IGxpc3QgdG8gc3RvcmUgdGhlIGRhdGEgZnJhbWVzCkNvdl9FZGdlc19MaXN0IDwtIGxpc3QoKQoKIyBMb29wIHRocm91Z2ggdGhlIGVkZ2UgdGFibGVzIGFuZCBwcm9jZXNzIGVhY2ggb25lCmZvciAoaSBpbiAxOjEwKSB7CiAgZWRnZV90YWJsZSA8LSBhcy5kYXRhLmZyYW1lKExfb2JqX3NpbTVbWyJFZGdlX1RhYmxlIl1dW1twYXN0ZTAoIkVkZ2VfVGFibGVfIiwgaSldXSkKICAjIENoZWNrIGlmIHRoZSBlZGdlIHRhYmxlIGlzIGVtcHR5CiAgaWYgKG5yb3coZWRnZV90YWJsZSkgPT0gMCkgewogICAgIyBDcmVhdGUgYSBkYXRhIGZyYW1lIHdpdGggTkEgdmFsdWVzCiAgICBlZGdlX3RhYmxlIDwtIGRhdGEuZnJhbWUoRWRnZV93ZWlnaHQgPSBOQSwgVGltZSA9IGkpCiAgfSBlbHNlIHsKICAgICMgZHBseXI6OnJlbmFtZSB0aGUgY29sdW1uIGFuZCBhZGQgdGhlIFRpbWUgY29sdW1uCiAgICBlZGdlX3RhYmxlIDwtIGVkZ2VfdGFibGUgJT4lIGRwbHlyOjpyZW5hbWUoIkNvdl9FZGdlX3dlaWdodCI9ICJFZGdlX3dlaWdodCIpCiAgICBlZGdlX3RhYmxlJFRpbWUgPC0gaQogIH0KICAjIEFwcGVuZCB0aGUgcHJvY2Vzc2VkIHRhYmxlIHRvIHRoZSBsaXN0CiAgQ292X0VkZ2VzX0xpc3RbW2ldXSA8LSBlZGdlX3RhYmxlCn0KIyBDb21iaW5lIGFsbCB0aGUgcHJvY2Vzc2VkIHRhYmxlcyBpbnRvIGEgc2luZ2xlIGRhdGEgZnJhbWUKQ292X0VkZ2VzIDwtIGJpbmRfcm93cyhDb3ZfRWRnZXNfTGlzdCkKCiMgT3JpZ2luYWwgRGF0YSAtIEV4dHJhY3QgRWRnZXMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBJbml0aWFsaXplIGFuIGVtcHR5IGxpc3QgdG8gc3RvcmUgdGhlIGRhdGEgZnJhbWVzCk9fRWRnZXNfTGlzdCA8LSBsaXN0KCkKCiMgTG9vcCB0aHJvdWdoIHRoZSBlZGdlIHRhYmxlcyBhbmQgcHJvY2VzcyBlYWNoIG9uZQpmb3IgKGkgaW4gMToxMCkgewogIGVkZ2VfdGFibGUgPC0gYXMuZGF0YS5mcmFtZShMX29ial9zaW00W1siRWRnZV9UYWJsZSJdXVtbcGFzdGUwKCJFZGdlX1RhYmxlXyIsIGkpXV0pCiAgIyBDaGVjayBpZiB0aGUgZWRnZSB0YWJsZSBpcyBlbXB0eQogIGlmIChucm93KGVkZ2VfdGFibGUpID09IDApIHsKICAgICMgQ3JlYXRlIGEgZGF0YSBmcmFtZSB3aXRoIE5BIHZhbHVlcwogICAgZWRnZV90YWJsZSA8LSBkYXRhLmZyYW1lKEVkZ2Vfd2VpZ2h0ID0gTkEsIFRpbWUgPSBpKQogIH0gZWxzZSB7CiAgICAjIGRwbHlyOjpyZW5hbWUgdGhlIGNvbHVtbiBhbmQgYWRkIHRoZSBUaW1lIGNvbHVtbgogICAgZWRnZV90YWJsZSA8LSBlZGdlX3RhYmxlICU+JSBkcGx5cjo6cmVuYW1lKCJPX0VkZ2Vfd2VpZ2h0Ij0gIkVkZ2Vfd2VpZ2h0IikKICAgIGVkZ2VfdGFibGUkVGltZSA8LSBpCiAgfQogICMgQXBwZW5kIHRoZSBwcm9jZXNzZWQgdGFibGUgdG8gdGhlIGxpc3QKICBPX0VkZ2VzX0xpc3RbW2ldXSA8LSBlZGdlX3RhYmxlCn0KIyBDb21iaW5lIGFsbCB0aGUgcHJvY2Vzc2VkIHRhYmxlcyBpbnRvIGEgc2luZ2xlIGRhdGEgZnJhbWUKT19FZGdlcyA8LSBiaW5kX3Jvd3MoT19FZGdlc19MaXN0KQoKIyBCaW5kIGRhdGEgdG9nZXRoZXIKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKTWVyZ2VkX1QxIDwtIG1lcmdlKExfRWRnZXMsIE9fRWRnZXMsIGJ5LnkgPSBjKCJTb3VyY2UiLCAiU2luayIsICJUaW1lIiksIGFsbD1UUlVFKQpNZXJnZWRfVDEgPC0gbWVyZ2UoTWVyZ2VkX1QxLCBDb3ZfRWRnZXMsIGJ5LnkgPSBjKCJTb3VyY2UiLCAiU2luayIsICJUaW1lIiksIGFsbD1UUlVFKQoKCiMgQ29tcGFyaXNvbiBHcmFwaCBEYXRhCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIElkZW50aWZ5IHRydWUgZWRnZXMKTWVyZ2VkX1Q3NSA8LSBNZXJnZWRfVDEgJT4lCiAgZWRnZV9yZWNvdmVyeSgpIAoKIyBBZGQgU2FtcGxlU2l6ZSAKTWVyZ2VkX1Q3NSRTYW1wbGVTaXplIDwtIDc1CmBgYAoKCgojIyAyLjUgLSBTdWJqZWN0cyBOPTEwMAoKIyMjIDIuNS4xIC0gTElNT04gb24gQ292YXJpYXRlIEluZmxhdGVkIERhdGEKUnVuIHRoZSBMSU1PTiBjb3ZhcmlhdGUgY29ycmVjdGlvbiBhbmQgbmV0d29yayBpbmZlcmVuY2Ugc3RlcHMgb24gQ292YXJpYXRlIGluZmxhdGVkIGRhdGEuIFRoZXNlIGVkZ2VzIHdpbGwgYmUgcmVxdWlyZWQgYXMgTF9FZGdlcyAgCgoKTWFrZSBMSU1PTiBPYmplY3QKYGBge3J9ClNpbURhdGEgPC0gcmVhZC5jc3YoaGVyZSgiRGF0YSIsICJHTFZfU2ltRGF0YSIsICJEYXRhc2V0XzIiLCJHTFZfQ292X04xMDAuY3N2IikpClNpbURhdGEgPC0gY29sdW1uX3RvX3Jvd25hbWVzKFNpbURhdGEsICJYIikKCiMgbWV0YWRhdGEKbWV0YV9kYXRhIDwtIFNpbURhdGFbLDE6NV0KbWV0YV9kYXRhJFRpbWUgPC0gYXMubnVtZXJpYyhtZXRhX2RhdGEkVGltZSkKbWV0YV9kYXRhJEJNSSA8LSBhcy5udW1lcmljKG1ldGFfZGF0YSRCTUkpCm1ldGFfZGF0YSRBZ2UgPC0gYXMubnVtZXJpYyhtZXRhX2RhdGEkQWdlKQoKIyBPcHRpb24gdG8gc2hvcnRlbiB0byBydW4gZmFzdGVyCmxpbW9uX21ldGEgPC0gbWV0YV9kYXRhICMlPiUgZmlsdGVyKFRpbWUgJWluJSBjKDEsMiwzLDQsNSkpCgojIENvdW50IGRhdGEKbGltb25fY291bnRzIDwtIGFzLm1hdHJpeChTaW1EYXRhWyw2OjU1XSkKCiMgc29ydCByb3dzCmxpbW9uX2NvdW50cyA8LSBsaW1vbl9jb3VudHNbcm93bmFtZXMobGltb25fbWV0YSksXQoKICAKTF9vYmogPC0gTElNT05fT2JqKENvdW50cyA9IGxpbW9uX2NvdW50cywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFNhbXBsZURhdGEgPSBsaW1vbl9tZXRhKQpgYGAKCgpEaXN0cmlidXRpb24gRml0dGluZyBhbmQgY2hlY2sKYGBge3J9CiMgU2V0IHNlZWQKc2V0LnNlZWQoMTIzNDUpCiMgRml0IHRoZSBkaXN0cmlidXRpb24vcmVtb3ZlIGNvdmFyaWF0ZXMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCkxfb2JqMiA8LSBMSU1PTl9EaXN0ckZpdChPYmogPSBMX29iaiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFRpbWUgPSAiVGltZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICBTdWJqZWN0ID0gIklEIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIENvdmFyaWF0ZXMgPSBjKCJTZXgiLCAiQk1JIiwgIkFnZSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBtb2RlbCA9ICJTZXgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBkaXN0cmlidXRpb24gPSAiTE1NIikKYGBgCgoKCgpOZXR3b3JrcyBvZiBMSU1PTiBEYXRhCmBgYHtyfQojIFNldCBzZWVkCnNldC5zZWVkKDEyMzQ1KQpwc2VlZCA8LSBsaXN0KHJlcC5udW09NTAsIHNlZWQ9MTAwMTApCgojIFNQSUVDLUVBU0kgcGVyIHRpbWUKTF9vYmozIDwtIExJTU9OX05ldEluZl9UaW1lKE9iaiA9IExfb2JqMiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gImdsYXNzbyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbC5jcml0ZXJpb24gPSAiYnN0YXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYW1iZGEubWluLnJhdGlvID0gMC4wMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdWxzYXIuc2VsZWN0PVRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHB1bHNhci5wYXJhbXM9cHNlZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmxhbWJkYSA9IDIwMCkKCgojIFByaW50IE5ldHdvcmtzCkxfb2JqNCA8LSBMSU1PTl9FZGdlc19OZXR3b3JrcyhMX29iajMsIHRocmVzaG9sZCA9IDAuMDAwMiwgdmVydGV4LnNpemUgPSAzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVydGV4LmxhYmVsLmNleCA9IDgsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJ0ZXgubGFiZWwuY29sb3IgPSAiYmxhY2siKQpgYGAKCgojIyMgMi41LjIgLSBUcnVlIE5ldHdvcmtzIC0gbm8gQ292YXJpYXRlcyAgCgpTUElFQy1FQVNJIE5ldHdvcmtzIG9mIHRoZSBSYXcgRGF0YSB3aXRob3V0IGNvdmFyaWF0ZXMuIFRoZXNlIHdpbGwgYmUgbGFiZWxlZCBhcyBPX0VkZ2VzIChmb3Igb3JpZ2luYWwgZWRnZXMpLiBXZSByZWFkIGluIHRoZSBkYXRhIGFuZCB0aGVuIGFzc2lnbiBpdCB0byBhIGRvd25zdHJlYW0gTElNT24gb2JqZWN0IHRvIHJ1biBTUElFQy1FQVNJIGFjcm9zcyBhbGwgdGhlIHRpbWVwb2ludHMgYnV0IGJ5cGFzc2luZyB0aGUgbmV0d29yayBpbmZlcmVuY2Ugc3RlcApgYGB7cn0KIyBNYWtlIGZha2UgTElNT04gT2JqZWN0IHRvIFBhc3MgdG8gTmV0d29yayBJbmZlcmVuY2UgU3RlcHMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpvcmlnaW5hbF9kYXRhIDwtIHJlYWQuY3N2KGhlcmUoIkRhdGEiLCAiR0xWX1NpbURhdGEiLCAiRGF0YXNldF8yIiwiR0xWX04xMDAuY3N2IikpCm9yaWdpbmFsX2RhdGEgPC0gb3JpZ2luYWxfZGF0YSAlPiUgY29sdW1uX3RvX3Jvd25hbWVzKCJYIikKCiMgTG9vcCB0aHJvdWdoIHRoZSB0aW1lIHBvaW50cywgZmlsdGVyIGRhdGEsIGFuZCByZW1vdmUgbWV0YWRhdGEKY291bnRzX2xpc3QgPC0gbGlzdCgpCmZvciAoaSBpbiAxOjEwKSB7CiAgZmlsdGVyZWRfY291bnRzIDwtIG9yaWdpbmFsX2RhdGEgJT4lIGZpbHRlcihUaW1lID09IGkpCiAgY291bnRzX2xpc3RbW2ldXSA8LSByb3VuZChmaWx0ZXJlZF9jb3VudHNbLCA2OjU1XSkKfQoKIyBJbml0aWFsaXplIExfb2JqX3NpbTIKTF9vYmpfc2ltMiA8LSBMX29iajIKCiMgQXNzaWduIHRoZSBwcm9jZXNzZWQgZGF0YSB0byB0aGUgY29ycmVzcG9uZGluZyBzbG90cyBpbiBMX29ial9zaW0yCmZvciAoaSBpbiAxOjEwKSB7CiAgTF9vYmpfc2ltMltbIkNvcnJlY3RlZF9Db3VudHNfVGltZSJdXVtbcGFzdGUwKCJDb3JyZWN0ZWRfQ291bnRzXyIsIGkpXV0gPC0gY291bnRzX2xpc3RbW2ldXQp9CgoKIyBOZXR3b3JrIEluZmVyZW5jZQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU2V0IHNlZWQKc2V0LnNlZWQoMTIzNDUpCnBzZWVkIDwtIGxpc3QocmVwLm51bT01MCwgc2VlZD0xMDAxMCkKIyBTUElFQy1FQVNJIHBlciB0aW1lCkxfb2JqX3NpbTMgPC0gTElNT05fTmV0SW5mX1RpbWUoT2JqID0gTF9vYmpfc2ltMiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gImdsYXNzbyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbC5jcml0ZXJpb24gPSAiYnN0YXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYW1iZGEubWluLnJhdGlvID0gMC4wMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdWxzYXIuc2VsZWN0PVRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHB1bHNhci5wYXJhbXM9cHNlZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmxhbWJkYSA9IDIwMCkKCgojIFByaW50IE5ldHdvcmtzCkxfb2JqX3NpbTQgPC0gTElNT05fRWRnZXNfTmV0d29ya3MoTF9vYmpfc2ltMywgdGhyZXNob2xkID0gMC4wMDAyLCB2ZXJ0ZXguc2l6ZSA9IDMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJ0ZXgubGFiZWwuY2V4ID0gOCwgdmVydGV4LmxhYmVsLmNvbG9yID0gImJsYWNrIikKCgoKYGBgCgoKCiMjIyAyLjUuMyAtIFNQSUVDLUVBU0kgb24gQ292YXJpYXRlIERhdGEgIApTaW1pbGFyIHRvIHByb2Nlc3MgZm9yIG9yaWdpbmFsIG5ldHdvcmtzLCBleGNlcHQgbm93IHdlIHdpbGwgcnVuIGl0IG9uIGNvdmFyaWF0ZSBjb25mbGF0ZWQgZGF0YS4gVGhlc2Ugd2lsbCBiZSBsYWJlbGVkIGFzIENvdl9FZGdlcwpgYGB7cn0KIyBNYWtlIGZha2UgTElNT04gT2JqZWN0IHRvIFBhc3MgdG8gTmV0d29yayBJbmZlcmVuY2UgU3RlcHMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpvcmlnaW5hbF9kYXRhIDwtIHJlYWQuY3N2KGhlcmUoIkRhdGEiLCAiR0xWX1NpbURhdGEiLCAiRGF0YXNldF8yIiwiR0xWX0Nvdl9OMTAwLmNzdiIpKQpvcmlnaW5hbF9kYXRhIDwtIG9yaWdpbmFsX2RhdGEgJT4lIGNvbHVtbl90b19yb3duYW1lcygiWCIpCgojIExvb3AgdGhyb3VnaCB0aGUgdGltZSBwb2ludHMsIGZpbHRlciBkYXRhLCBhbmQgcmVtb3ZlIG1ldGFkYXRhCmNvdW50c19saXN0IDwtIGxpc3QoKQpmb3IgKGkgaW4gMToxMCkgewogIGZpbHRlcmVkX2NvdW50cyA8LSBvcmlnaW5hbF9kYXRhICU+JSBmaWx0ZXIoVGltZSA9PSBpKQogIGNvdW50c19saXN0W1tpXV0gPC0gcm91bmQoZmlsdGVyZWRfY291bnRzWywgNjo1NV0pCn0KCiMgSW5pdGlhbGl6ZSBMX29ial9zaW0yCkxfb2JqX3NpbTIgPC0gTF9vYmoyCgojIEFzc2lnbiB0aGUgcHJvY2Vzc2VkIGRhdGEgdG8gdGhlIGNvcnJlc3BvbmRpbmcgc2xvdHMgaW4gTF9vYmpfc2ltMgpmb3IgKGkgaW4gMToxMCkgewogIExfb2JqX3NpbTJbWyJDb3JyZWN0ZWRfQ291bnRzX1RpbWUiXV1bW3Bhc3RlMCgiQ29ycmVjdGVkX0NvdW50c18iLCBpKV1dIDwtIGNvdW50c19saXN0W1tpXV0KfQoKCiMgTmV0d29yayBJbmZlcmVuY2UKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFNldCBzZWVkCnNldC5zZWVkKDEyMzQ1KQpwc2VlZCA8LSBsaXN0KHJlcC5udW09NTAsIHNlZWQ9MTAwMTApCiMgU1BJRUMtRUFTSSBwZXIgdGltZQpMX29ial9zaW0zIDwtIExJTU9OX05ldEluZl9UaW1lKE9iaiA9IExfb2JqX3NpbTIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJnbGFzc28iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWwuY3JpdGVyaW9uID0gImJzdGFycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFtYmRhLm1pbi5yYXRpbyA9IDAuMDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHVsc2FyLnNlbGVjdD1UUlVFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdWxzYXIucGFyYW1zPXBzZWVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5sYW1iZGEgPSAyMDApCgoKIyBQcmludCBOZXR3b3JrcwpMX29ial9zaW01IDwtIExJTU9OX0VkZ2VzX05ldHdvcmtzKExfb2JqX3NpbTMsIHRocmVzaG9sZCA9IDAuMDAwMiwgdmVydGV4LnNpemUgPSAzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVydGV4LmxhYmVsLmNleCA9IDgsIHZlcnRleC5sYWJlbC5jb2xvciA9ICJibGFjayIpCgoKCmBgYAoKCiMjIyAyLjUuNCAtIE1lcmdlIE91dGNvbWVzCkNhbGN1bGF0ZSB0aGUgc2ltaWxhciBlZGdlcyBhY3Jvc3MgdGhlIHRocmVlIGRhdGEgdHlwZXMKYGBge3J9CiMgTElNT04gRGF0YSAtIEV4dHJhY3QgRWRnZXMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCiMgSW5pdGlhbGl6ZSBhbiBlbXB0eSBsaXN0IHRvIHN0b3JlIHRoZSBkYXRhIGZyYW1lcwpMX0VkZ2VzX0xpc3QgPC0gbGlzdCgpCgojIExvb3AgdGhyb3VnaCB0aGUgZWRnZSB0YWJsZXMgYW5kIHByb2Nlc3MgZWFjaCBvbmUKZm9yIChpIGluIDE6MTApIHsKICBlZGdlX3RhYmxlIDwtIGFzLmRhdGEuZnJhbWUoTF9vYmo0W1siRWRnZV9UYWJsZSJdXVtbcGFzdGUwKCJFZGdlX1RhYmxlXyIsIGkpXV0pCiAgIyBDaGVjayBpZiB0aGUgZWRnZSB0YWJsZSBpcyBlbXB0eQogIGlmIChucm93KGVkZ2VfdGFibGUpID09IDApIHsKICAgICMgQ3JlYXRlIGEgZGF0YSBmcmFtZSB3aXRoIE5BIHZhbHVlcwogICAgZWRnZV90YWJsZSA8LSBkYXRhLmZyYW1lKEVkZ2Vfd2VpZ2h0ID0gTkEsIFRpbWUgPSBpKQogIH0gZWxzZSB7CiAgICAjIGRwbHlyOjpyZW5hbWUgdGhlIGNvbHVtbiBhbmQgYWRkIHRoZSBUaW1lIGNvbHVtbgogICAgZWRnZV90YWJsZSA8LSBlZGdlX3RhYmxlICU+JSBkcGx5cjo6cmVuYW1lKCJMX0VkZ2Vfd2VpZ2h0Ij0gIkVkZ2Vfd2VpZ2h0IikKICAgIGVkZ2VfdGFibGUkVGltZSA8LSBpCiAgfQogICMgQXBwZW5kIHRoZSBwcm9jZXNzZWQgdGFibGUgdG8gdGhlIGxpc3QKICBMX0VkZ2VzX0xpc3RbW2ldXSA8LSBlZGdlX3RhYmxlCn0KIyBDb21iaW5lIGFsbCB0aGUgcHJvY2Vzc2VkIHRhYmxlcyBpbnRvIGEgc2luZ2xlIGRhdGEgZnJhbWUKTF9FZGdlcyA8LSBiaW5kX3Jvd3MoTF9FZGdlc19MaXN0KQoKIyBPcmlnaW5hbCBEYXRhICsgQ292IC0gRXh0cmFjdCBlZGdlcwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyBJbml0aWFsaXplIGFuIGVtcHR5IGxpc3QgdG8gc3RvcmUgdGhlIGRhdGEgZnJhbWVzCkNvdl9FZGdlc19MaXN0IDwtIGxpc3QoKQoKIyBMb29wIHRocm91Z2ggdGhlIGVkZ2UgdGFibGVzIGFuZCBwcm9jZXNzIGVhY2ggb25lCmZvciAoaSBpbiAxOjEwKSB7CiAgZWRnZV90YWJsZSA8LSBhcy5kYXRhLmZyYW1lKExfb2JqX3NpbTVbWyJFZGdlX1RhYmxlIl1dW1twYXN0ZTAoIkVkZ2VfVGFibGVfIiwgaSldXSkKICAjIENoZWNrIGlmIHRoZSBlZGdlIHRhYmxlIGlzIGVtcHR5CiAgaWYgKG5yb3coZWRnZV90YWJsZSkgPT0gMCkgewogICAgIyBDcmVhdGUgYSBkYXRhIGZyYW1lIHdpdGggTkEgdmFsdWVzCiAgICBlZGdlX3RhYmxlIDwtIGRhdGEuZnJhbWUoRWRnZV93ZWlnaHQgPSBOQSwgVGltZSA9IGkpCiAgfSBlbHNlIHsKICAgICMgZHBseXI6OnJlbmFtZSB0aGUgY29sdW1uIGFuZCBhZGQgdGhlIFRpbWUgY29sdW1uCiAgICBlZGdlX3RhYmxlIDwtIGVkZ2VfdGFibGUgJT4lIGRwbHlyOjpyZW5hbWUoIkNvdl9FZGdlX3dlaWdodCI9ICJFZGdlX3dlaWdodCIpCiAgICBlZGdlX3RhYmxlJFRpbWUgPC0gaQogIH0KICAjIEFwcGVuZCB0aGUgcHJvY2Vzc2VkIHRhYmxlIHRvIHRoZSBsaXN0CiAgQ292X0VkZ2VzX0xpc3RbW2ldXSA8LSBlZGdlX3RhYmxlCn0KIyBDb21iaW5lIGFsbCB0aGUgcHJvY2Vzc2VkIHRhYmxlcyBpbnRvIGEgc2luZ2xlIGRhdGEgZnJhbWUKQ292X0VkZ2VzIDwtIGJpbmRfcm93cyhDb3ZfRWRnZXNfTGlzdCkKCiMgT3JpZ2luYWwgRGF0YSAtIEV4dHJhY3QgRWRnZXMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBJbml0aWFsaXplIGFuIGVtcHR5IGxpc3QgdG8gc3RvcmUgdGhlIGRhdGEgZnJhbWVzCk9fRWRnZXNfTGlzdCA8LSBsaXN0KCkKCiMgTG9vcCB0aHJvdWdoIHRoZSBlZGdlIHRhYmxlcyBhbmQgcHJvY2VzcyBlYWNoIG9uZQpmb3IgKGkgaW4gMToxMCkgewogIGVkZ2VfdGFibGUgPC0gYXMuZGF0YS5mcmFtZShMX29ial9zaW00W1siRWRnZV9UYWJsZSJdXVtbcGFzdGUwKCJFZGdlX1RhYmxlXyIsIGkpXV0pCiAgIyBDaGVjayBpZiB0aGUgZWRnZSB0YWJsZSBpcyBlbXB0eQogIGlmIChucm93KGVkZ2VfdGFibGUpID09IDApIHsKICAgICMgQ3JlYXRlIGEgZGF0YSBmcmFtZSB3aXRoIE5BIHZhbHVlcwogICAgZWRnZV90YWJsZSA8LSBkYXRhLmZyYW1lKEVkZ2Vfd2VpZ2h0ID0gTkEsIFRpbWUgPSBpKQogIH0gZWxzZSB7CiAgICAjIGRwbHlyOjpyZW5hbWUgdGhlIGNvbHVtbiBhbmQgYWRkIHRoZSBUaW1lIGNvbHVtbgogICAgZWRnZV90YWJsZSA8LSBlZGdlX3RhYmxlICU+JSBkcGx5cjo6cmVuYW1lKCJPX0VkZ2Vfd2VpZ2h0Ij0gIkVkZ2Vfd2VpZ2h0IikKICAgIGVkZ2VfdGFibGUkVGltZSA8LSBpCiAgfQogICMgQXBwZW5kIHRoZSBwcm9jZXNzZWQgdGFibGUgdG8gdGhlIGxpc3QKICBPX0VkZ2VzX0xpc3RbW2ldXSA8LSBlZGdlX3RhYmxlCn0KIyBDb21iaW5lIGFsbCB0aGUgcHJvY2Vzc2VkIHRhYmxlcyBpbnRvIGEgc2luZ2xlIGRhdGEgZnJhbWUKT19FZGdlcyA8LSBiaW5kX3Jvd3MoT19FZGdlc19MaXN0KQoKIyBCaW5kIGRhdGEgdG9nZXRoZXIKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKTWVyZ2VkX1QxIDwtIG1lcmdlKExfRWRnZXMsIE9fRWRnZXMsIGJ5LnkgPSBjKCJTb3VyY2UiLCAiU2luayIsICJUaW1lIiksIGFsbD1UUlVFKQpNZXJnZWRfVDEgPC0gbWVyZ2UoTWVyZ2VkX1QxLCBDb3ZfRWRnZXMsIGJ5LnkgPSBjKCJTb3VyY2UiLCAiU2luayIsICJUaW1lIiksIGFsbD1UUlVFKQoKCiMgQ29tcGFyaXNvbiBHcmFwaCBEYXRhCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIElkZW50aWZ5IHRydWUgZWRnZXMKTWVyZ2VkX1QxMDAgPC0gTWVyZ2VkX1QxICU+JQogIGVkZ2VfcmVjb3ZlcnkoKSAKCiMgQWRkIFNhbXBsZVNpemUgCk1lcmdlZF9UMTAwJFNhbXBsZVNpemUgPC0gMTAwCmBgYAoKCiMgMyAtIFN1bW1hcnkgb2YgU2Vuc2l0aXZ0eSBBbmFseXNpcwoqKioKTWVyZ2UgZGF0YSBmb3IgdGhlIGZvbGxvd2luZyBncmFwaGljYWwgYW5hbHlzaXMKYGBge3J9CiMgRHJvcCBTb3VyY2UgYW5kIFNpbmsgY29sdW1uIGFuZCBjb21iaW5lIHRvZ2V0aGVyCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCk1lcmdlZF9UMWIgPC0gTWVyZ2VkX1QxMCAlPiUgZHBseXI6OnNlbGVjdCgtU291cmNlLCAtU2luaykKTWVyZ2VkX1QyMGIgPC0gTWVyZ2VkX1QyMCAlPiUgZHBseXI6OnNlbGVjdCgtU291cmNlLCAtU2luaykKTWVyZ2VkX1Q1MGIgPC0gTWVyZ2VkX1Q1MCAlPiUgZHBseXI6OnNlbGVjdCgtU291cmNlLCAtU2luaykKTWVyZ2VkX1Q3NWIgPC0gTWVyZ2VkX1Q3NSAlPiUgZHBseXI6OnNlbGVjdCgtU291cmNlLCAtU2luaykKTWVyZ2VkX1QxMDBiIDwtIE1lcmdlZF9UMTAwICU+JSBkcGx5cjo6c2VsZWN0KC1Tb3VyY2UsIC1TaW5rKQoKIyBNZXJnZSB0b2dldGhlcgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIERhdGEgZm9yIEdyYXBocyAxIGFuZCAyClN1YmplY3Rfc2Vuc19kYXRhX2Z1bGwgPC0gcmJpbmQoTWVyZ2VkX1QxYiwgTWVyZ2VkX1QyMGIsIE1lcmdlZF9UNTBiLCBNZXJnZWRfVDc1YiwgTWVyZ2VkX1QxMDBiKQoKIyBEYXRhIGZvciBHcmFwaCAzCiNNZXJnZWRfVDEwMGMgPC0gTWVyZ2VkX1QxMDAgJT4lIGRwbHlyOjpzZWxlY3QoLUVkZ2Vfd2VpZ2h0KQpNZXJnZWRfVDEwYyA8LSBNZXJnZWRfVDEwWyxjKCJTb3VyY2UiLCJTaW5rIiwiVGltZSIsIkxfRWRnZV93ZWlnaHQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIk9fRWRnZV93ZWlnaHQiLCJDb3ZfRWRnZV93ZWlnaHQiLCJSZWNvdmVyeSIsIlNhbXBsZVNpemUiKV0KU3ViamVjdF9zZW5zX2RhdGFfdGF4YSA8LSByYmluZChNZXJnZWRfVDEwYywgTWVyZ2VkX1QyMCwgTWVyZ2VkX1Q1MCwgTWVyZ2VkX1Q3NSwgTWVyZ2VkX1QxMDApCmBgYAoKCk9wdGlvbiB0byB3cml0ZSBhbmQgcmVhZCBpbiB0aGUgYWJvdmUgZmlsZXMKYGBge3J9CiN3cml0ZS5jc3YoU3ViamVjdF9zZW5zX2RhdGFfZnVsbCwgaGVyZSgiT3V0cHV0IiwgIkRhdGFzZXRfMiIsICJTdWJqZWN0X3NlbnNfZGF0YV9mdWxsLmNzdiIpKQojd3JpdGUuY3N2KFN1YmplY3Rfc2Vuc19kYXRhX3RheGEsIGhlcmUoIk91dHB1dCIsICJEYXRhc2V0XzIiLCJTdWJqZWN0X3NlbnNfZGF0YV90YXhhLmNzdiIpKQpTdWJqZWN0X3NlbnNfZGF0YV9mdWxsIDwtIHJlYWQuY3N2KGhlcmUoIk91dHB1dCIsICJEYXRhc2V0XzIiLCAiU3ViamVjdF9zZW5zX2RhdGFfZnVsbC5jc3YiKSkKU3ViamVjdF9zZW5zX2RhdGFfdGF4YSA8LSByZWFkLmNzdihoZXJlKCJPdXRwdXQiLCJEYXRhc2V0XzIiLCAiU3ViamVjdF9zZW5zX2RhdGFfdGF4YS5jc3YiKSkKYGBgCgoKCgojIyAzLjEgR3JhcGggUGVyY2VudCBUcnVlIEVkZ2VzCgpGaW5kIHRoZSBQZXJjZW50IFRydWUgRWRnZXMgUmVjb3ZlcmVkIHBlciBzYW1wbGUgc2l6ZSBwZXIgdGltZSBwb2ludAoKCiMjIyAzLjEuMSAtIERhdGEgRm9ybWF0dGluZwoKYGBge3J9CiMgTG9vcCB0aHJvdWdoIGFsbCB0aW1lcG9pbnRzCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKYWxsX3RpbWVwb2ludHMgPC0gbGlzdCgpCgpmb3IgKHRpbWUgaW4gMToxMCkgewogICMgRmlsdGVyIGRhdGEgZm9yIHRoZSBjdXJyZW50IFNhbXBsZSBTaXplCiAgU3ViamVjdF9zZW5zX2RhdGEgPC0gU3ViamVjdF9zZW5zX2RhdGFfZnVsbCAlPiUgZmlsdGVyKFRpbWUgPT0gdGltZSkKICAKICAjIENvdW50dGhlIHRvdGFsIFRydWVfRWRnZXMgZm9yIHRoYXQgU2FtcGxlIFNpemUKICAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiAgCiAgU3ViamVjdF9zZW5zX2RhdGEyIDwtIFN1YmplY3Rfc2Vuc19kYXRhICU+JQogICAgZ3JvdXBfYnkoU2FtcGxlU2l6ZSkgJT4lCiAgICBzdW1tYXJpc2UoVHJ1ZV9FZGdlcyA9IHN1bShPX0VkZ2Vfd2VpZ2h0ICE9IDAsIG5hLnJtID0gVFJVRSkpICU+JQogICAgdW5ncm91cCgpCiAgCiAgIyBNZXJnZSB0aGUgc3VtbWFyaXplZCBkYXRhIGJhY2sgd2l0aCB0aGUgb3JpZ2luYWwgZGF0YQogIFN1YmplY3Rfc2Vuc19kYXRhIDwtIG1lcmdlKFN1YmplY3Rfc2Vuc19kYXRhLCBTdWJqZWN0X3NlbnNfZGF0YTIsIGJ5PSJTYW1wbGVTaXplIiwgYWxsPVRSVUUpCiAgCiAgIyBGaW5kIHRoZSBQZXJjZW50IFRydWUgRWRnZXMgRGF0YQogICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKICAKICAjIGNvdW50IHRoZSBudW1iZXIgb2YgZWRnZXMKICBQZXJjZW50X3RydWUgPC0gU3ViamVjdF9zZW5zX2RhdGEgICU+JQogICAgZ3JvdXBfYnkoU2FtcGxlU2l6ZSwgUmVjb3ZlcnksIFRydWVfRWRnZXMpICU+JQogICAgc3VtbWFyaXNlKENvdW50ID0gbigpKSAlPiUKICAgIHVuZ3JvdXAoKSAlPiUKICAgIGZpbHRlcihSZWNvdmVyeSAhPSAwKQogIAogICMgQWRkIHRoZSBudW1iZXIgb2YgZWRnZXMgaW4gYWxsIHRocmVlIGRhdGEgdHlwZXMgdG8gdGhlIExJTU9OIHZzIFJhdyBTcGVpYy1FYXNpIGNvdW50cwogIFBlcmNlbnRfdHJ1ZSA8LSBQZXJjZW50X3RydWUgJT4lCiAgICBncm91cF9ieShTYW1wbGVTaXplLCBUcnVlX0VkZ2VzKSAlPiUKICAgIG11dGF0ZSgKICAgICAgQ291bnRfMyA9IGlmZWxzZShSZWNvdmVyeSA9PSAzLCBDb3VudCwgMCksCiAgICAgIENvdW50ID0gaWZlbHNlKFJlY292ZXJ5ID09IDEgfCBSZWNvdmVyeSA9PSAyLCBDb3VudCArIHN1bShDb3VudF8zKSwgQ291bnQpCiAgICApICU+JQogICAgZHBseXI6OnNlbGVjdCgtQ291bnRfMykKICAKICAjIENyZWF0ZSBhIGRhdGFmcmFtZSB3aXRoIGFsbCBjb21iaW5hdGlvbnMgb2YgU2FtcGxlU2l6ZSBhbmQgUmVjb3ZlcnkKICBjb21iaW5hdGlvbnMgPC0gZXhwYW5kLmdyaWQoU2FtcGxlU2l6ZSA9IHVuaXF1ZShQZXJjZW50X3RydWUkU2FtcGxlU2l6ZSksIFJlY292ZXJ5ID0gYygxLCAyLCAzKSkKICBQZXJjZW50X3RydWUgPC0gbWVyZ2UoUGVyY2VudF90cnVlLCBjb21iaW5hdGlvbnMsIGJ5PWMoIlNhbXBsZVNpemUiLCAiUmVjb3ZlcnkiKSwgYWxsPVRSVUUpCiAgCiAgIyBGaWxsIGFuZCByZXBsYWNlIG1pc3NpbmcgdmFsdWVzCiAgUGVyY2VudF90cnVlIDwtIFBlcmNlbnRfdHJ1ZSAlPiUKICAgIGdyb3VwX2J5KFNhbXBsZVNpemUpICU+JQogICAgZmlsbChUcnVlX0VkZ2VzLCAuZGlyZWN0aW9uID0gImRvd251cCIpICU+JQogICAgbXV0YXRlKAogICAgICBDb3VudCA9IGlmZWxzZShpcy5uYShDb3VudCksIENvdW50W1JlY292ZXJ5ID09IDNdWzFdLCBpZmVsc2UoaXMubmEoQ291bnQpLCAwLCBDb3VudCkpCiAgICApICU+JQogICAgdW5ncm91cCgpICU+JQogICAgcmVwbGFjZV9uYShsaXN0KENvdW50ID0gMCkpICU+JSAKICAgIG11dGF0ZShEYXRhVHlwZSA9IGNhc2Vfd2hlbigKICAgICAgUmVjb3ZlcnkgPT0gMSB+ICJMSU1PTiIsCiAgICAgIFJlY292ZXJ5ID09IDIgfiAiU1BJRUMtRUFTSSIsCiAgICAgIFJlY292ZXJ5ID09IDMgfiAiQm90aCIKICAgICkpCiAgCiAgIyBDYWxjdWxhdGUgdGhlIFBlcmNlbnRhZ2Ugb2YgZWRnZXMKICBQZXJjZW50X3RydWUkUGVyY2VudF90cnVlIDwtIFBlcmNlbnRfdHJ1ZSRDb3VudCAvIFBlcmNlbnRfdHJ1ZSRUcnVlX0VkZ2VzCiAgUGVyY2VudF90cnVlJFRpbWUgPC0gdGltZQogIAogICMgU3RvcmUgdGhlIHJlc3VsdCBpbiB0aGUgbGlzdAogIGFsbF90aW1lcG9pbnRzW1t0aW1lXV0gPC0gUGVyY2VudF90cnVlCn0KCiMgQ29tYmluZSBhbGwgdGltZXBvaW50cyB0b2dldGhlcgpQZXJjZW50X3RydWUgPC0gZG8uY2FsbChyYmluZCwgYWxsX3RpbWVwb2ludHMpCgojIEZpbHRlciB0aGUgY29tYmluZWQgZGF0YQpQZXJjZW50X3RydWUgPC0gUGVyY2VudF90cnVlICU+JSBmaWx0ZXIoRGF0YVR5cGUgIT0gIkJvdGgiKQpgYGAKCgoKCiMjIyAzLjEuMiAtIFN0YXRpc3RpY3MKCkNoZWNrIG5vcm1hbGN5IHRvIGRldGVybWluZSBpZiB0LXRlc3Qgb3IgaWYgd2lsY294IHJhbmsgc3VtCmBgYHtyfQojIFN0ZXAgMSAtIFJ1biBTaGFwaXJvIFdpbGtzIHRvIHRlc3QgZm9yIG5vcm1hbGFjeSBvZiB0aGUgZGF0YQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzaGFwaXJvX3Jlc3VsdCA8LSBzaGFwaXJvLnRlc3QoUGVyY2VudF90cnVlJFBlcmNlbnRfdHJ1ZSkKcF92YWx1ZSA8LSBzaGFwaXJvX3Jlc3VsdCRwLnZhbHVlCgojIFN0ZXAgMiAtIFByaW50IHAtdmFsdWUgb24gdGhlIGhpc3RvZ3JhbQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIENyZWF0ZSB0aGUgaGlzdG9ncmFtIHBsb3QKaGlzdChQZXJjZW50X3RydWUkUGVyY2VudF90cnVlLCBicmVha3M9MTAwLCAKICAgICBtYWluID0gIkRpc3RyaWJ1dGlvbiBvZiBQZXJjZW50IFRydWUiLCAKICAgICB4bGFiID0gIlBlcmNlbnQgVHJ1ZSIsIHlsYWIgPSAiRnJlcXVlbmN5IikKIyBBZGQgdGhlIHAtdmFsdWUgYW5ub3RhdGlvbgp0ZXh0KHggPSAwLjQsIHkgPSAxMCwgCiAgICAgbGFiZWxzID0gcGFzdGUoIlNoYXBpcm8tV2lsayBwLXZhbHVlOiAiLCBwX3ZhbHVlKSkKCmBgYAoKRGF0YSBpcyBub3Qgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgc28gdXNlIHdpbGNveCByYW5rIHN1bSB0ZXN0IGluc3RlYWQgb2YgdC10ZXN0CgoKcC12YWx1ZSBsYWJlbCBmdW5jdGlvbgpgYGB7cn0KIyBQLXZhbHVlIGxhYmVscwpwX3ZhbHVlX3NpZyA8LSBmdW5jdGlvbihwKSB7CiAgaWYgKGlzLm5hKHApKSB7CiAgICByZXR1cm4oIiAiKQogIH0gZWxzZSBpZiAocCA8IDAuMDAxKSB7CiAgICByZXR1cm4oIioqKiIpCiAgfSBlbHNlIGlmIChwIDwgMC4wMSkgewogICAgcmV0dXJuKCIqKiIpCiAgfSBlbHNlIGlmIChwIDwgMC4wNSkgewogICAgcmV0dXJuKCIqIikKICB9IGVsc2UgewogICAgcmV0dXJuKCIgIikKICB9Cn0KYGBgCgoKUGxvdCB0aGUgZmluZGluZ3MKYGBge3J9CgojIFN0ZXAgMTogU3VtbWFyeSBTdGF0aXN0aWNzCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojIFBlcmZvcm0gYSBULXRlc3RzIGFuZCBtYWtlIHN1bW1hcnkgc3RhdGlzdGljcyBmb3IgcGxvdHRpbmcKU3VtbWFyeV9kYXRhIDwtIFBlcmNlbnRfdHJ1ZSAlPiUKICBncm91cF9ieShTYW1wbGVTaXplKSAlPiUKICBkbyh7CiAgICBkYXRhID0gLgogICAgCiAgICAjIENoZWNrIGlmIHRoZSBkYXRhIGZvciBhbnkgZ3JvdXAgaXMgY29uc3RhbnQKICAgIGdyb3VwX3ZhcmlhbmNlcyA8LSBkYXRhICU+JQogICAgICBncm91cF9ieShEYXRhVHlwZSkgJT4lCiAgICAgIHN1bW1hcmlzZSh2YXIgPSB2YXIoUGVyY2VudF90cnVlLCBuYS5ybSA9IFRSVUUpKSAgIyBIYW5kbGUgTkFzIGluIHZhcmlhbmNlIGNhbGN1bGF0aW9uCiAgICAKICAgIGlmIChhbnkoaXMubmEoZ3JvdXBfdmFyaWFuY2VzJHZhcikpIHx8IGFueShncm91cF92YXJpYW5jZXMkdmFyID09IDApKSB7CiAgICAgICMgSWYgZGF0YSBpcyBjb25zdGFudCBvciB2YXJpYW5jZSBjYWxjdWxhdGlvbiByZXN1bHRlZCBpbiBOQSwgYXNzaWduIHAtdmFsdWUgYXMgTkEKICAgICAgcF92YWx1ZSA8LSBOQQogICAgfSBlbHNlIHsKICAgICAgIyBQZXJmb3JtIHQtdGVzdAogICAgICB3aWxjb3ggPC0gZXhhY3RSYW5rVGVzdHM6OndpbGNveC5leGFjdChQZXJjZW50X3RydWUgfiBEYXRhVHlwZSwgZGF0YSA9IGRhdGEpCiAgICAgIHBfdmFsdWUgPC0gdGlkeSh3aWxjb3gpJHAudmFsdWUKICAgIH0KICAgIAogICAgc3VtbWFyaXNlKGRhdGEsCiAgICAgIG1lYW5fcGVyY2VudF90cnVlX0xJTU9OID0gbWVhbihQZXJjZW50X3RydWVbRGF0YVR5cGUgPT0gIkxJTU9OIl0sIG5hLnJtID0gVFJVRSksCiAgICAgIHNkX3BlcmNlbnRfdHJ1ZV9MSU1PTiA9IHNkKFBlcmNlbnRfdHJ1ZVtEYXRhVHlwZSA9PSAiTElNT04iXSwgbmEucm0gPSBUUlVFKSwKICAgICAgbl9MSU1PTiA9IHN1bShEYXRhVHlwZSA9PSAiTElNT04iLCBuYS5ybSA9IFRSVUUpLAogICAgICBtZWFuX3BlcmNlbnRfdHJ1ZV9TUElFQ0VBU0kgPSBtZWFuKFBlcmNlbnRfdHJ1ZVtEYXRhVHlwZSA9PSAiU1BJRUMtRUFTSSJdLCBuYS5ybSA9IFRSVUUpLAogICAgICBzZF9wZXJjZW50X3RydWVfU1BJRUNFQVNJID0gc2QoUGVyY2VudF90cnVlW0RhdGFUeXBlID09ICJTUElFQy1FQVNJIl0sIG5hLnJtID0gVFJVRSksCiAgICAgIG5fU1BJRUNFQVNJID0gc3VtKERhdGFUeXBlID09ICJTUElFQy1FQVNJIiwgbmEucm0gPSBUUlVFKSwKICAgICAgcC52YWx1ZSA9IHBfdmFsdWUKICAgICkKICB9KSAlPiUKICBtdXRhdGUoCiAgICBzZV9wZXJjZW50X3RydWVfTElNT04gPSBzZF9wZXJjZW50X3RydWVfTElNT04gLyBzcXJ0KG5fTElNT04pLAogICAgc2VfcGVyY2VudF90cnVlX1NQSUVDRUFTSSA9IHNkX3BlcmNlbnRfdHJ1ZV9TUElFQ0VBU0kgLyBzcXJ0KG5fU1BJRUNFQVNJKSwKICAgIHNpZ25pZmljYW5jZSA9IHNhcHBseShwLnZhbHVlLCBwX3ZhbHVlX3NpZykKICApCgojIFN0ZXAgMjogTWFrZSB0aGUgcGxvdHRpbmcgZGF0YQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpTdW1tYXJ5X2RhdGFfbG9uZyA8LSBTdW1tYXJ5X2RhdGEgJT4lCiAgZHBseXI6OnNlbGVjdChTYW1wbGVTaXplLCBzdGFydHNfd2l0aCgibWVhbl9wZXJjZW50X3RydWUiKSwgc3RhcnRzX3dpdGgoInNlX3BlcmNlbnRfdHJ1ZSIpLCBzaWduaWZpY2FuY2UpICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gc3RhcnRzX3dpdGgoIm1lYW5fcGVyY2VudF90cnVlIiksIG5hbWVzX3RvID0gIkRhdGFUeXBlIiwgdmFsdWVzX3RvID0gIm1lYW5fcGVyY2VudF90cnVlIikgJT4lCiAgbXV0YXRlKERhdGFUeXBlID0gaWZlbHNlKHN0cl9kZXRlY3QoRGF0YVR5cGUsICJMSU1PTiIpLCAiTElNT04iLCAiU1BJRUMtRUFTSSIpKSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IHN0YXJ0c193aXRoKCJzZV9wZXJjZW50X3RydWUiKSwgbmFtZXNfdG8gPSAiRGF0YVR5cGVfc2UiLCB2YWx1ZXNfdG8gPSAic2VfcGVyY2VudF90cnVlIikgJT4lCiAgbXV0YXRlKERhdGFUeXBlX3NlID0gaWZlbHNlKHN0cl9kZXRlY3QoRGF0YVR5cGVfc2UsICJMSU1PTiIpLCAiTElNT04iLCAiU1BJRUMtRUFTSSIpKSAlPiUKICBmaWx0ZXIoRGF0YVR5cGUgPT0gRGF0YVR5cGVfc2UpICU+JQogIGRwbHlyOjpzZWxlY3QoLURhdGFUeXBlX3NlKQoKIyBTdGVwIDM6IEZpbmFsIGZpZ3VyZQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpnZ3Bsb3QoU3VtbWFyeV9kYXRhX2xvbmcsIGFlcyh4ID0gU2FtcGxlU2l6ZSwgeSA9IG1lYW5fcGVyY2VudF90cnVlLCBjb2xvciA9IERhdGFUeXBlKSkgKwogIGdlb21fbGluZSgpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fZXJyb3JiYXIoYWVzKHltaW4gPSBtZWFuX3BlcmNlbnRfdHJ1ZSAtIHNlX3BlcmNlbnRfdHJ1ZSwgeW1heCA9IG1lYW5fcGVyY2VudF90cnVlICsgc2VfcGVyY2VudF90cnVlKSwgd2lkdGggPSAxLjApICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gc2lnbmlmaWNhbmNlLCB5ID0gMC45KSwgc2l6ZSA9IDUsIAogICAgICAgICAgICB2anVzdCA9IC0wLjUsIGNoZWNrX292ZXJsYXAgPSBUUlVFLCBjb2xvciA9ICJibGFjayIpICsgIyBwLXZhbHVlIGFubm90YXRpb25zCiAgbGFicyh4ID0gIlNhbXBsZVNpemUiLCB5ID0gIlBlcmNlbnRfdHJ1ZSIsIGNvbG9yID0gIkRhdGFUeXBlIikgKwogIHlsaW0oMCwgMSkgKyAKICB4bGFiKCJTYW1wbGUgU2l6ZSIpICsKICB5bGFiKCJQZXJjZW50IFJlY292ZXJlZCBFZGdlcyIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZSA9ICJOZXR3b3JrIiwgdmFsdWVzID0gYygiTElNT04iID0gIm9yYW5nZSIsICJTUElFQy1FQVNJIiA9ICJibHVlIikpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygxMCwgMTAwKSwgYnJlYWtzID0gYygxMCwyMCw1MCw3NSwxMDApKSAgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gImFyaWFsIixjb2xvciA9ICJibGFjayIsIHNpemUgPSAxOCksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gImFyaWFsIixjb2xvciA9ICJibGFjayIsIHNpemUgPSAxOCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJhcmlhbCIsY29sb3IgPSAiYmxhY2siLCBzaXplID0gMTQpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiYXJpYWwiLGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDE0KSkKYGBgCgoKCgoKIyMgMy4yICBHcmFwaCBUb3RhbCBFZGdlcwoKIyMjIDMuMi4xIC0gRGF0YSBGb3JtYXR0aW5nCk1ha2UgRGF0YSBmcmFtZSBmb3IgVG90YWwgRWRnZXMgcGVyIGRhdGEgVHlwZSBieSBUaW1lIHBvaW50CmBgYHtyfQojIEluaXRpYWxpemUgYW4gZW1wdHkgbGlzdCB0byBzdG9yZSByZXN1bHRzCnRvdGFsX2VkZ2VzX2xpc3QgPC0gbGlzdCgpCgojIExvb3AgdGhyb3VnaCBlYWNoIHRpbWVwb2ludCBmcm9tIDEgdG8gMTAKZm9yICh0aW1lIGluIDE6MTApIHsKICAjIENhbGN1bGF0ZSBUb3RhbCBFZGdlcyBmb3IgdGhlIGN1cnJlbnQgdGltZXBvaW50CiAgdG90YWxfZWRnZXMgPC0gU3ViamVjdF9zZW5zX2RhdGFfZnVsbCAlPiUKICAgIGZpbHRlcihUaW1lID09IHRpbWUpICU+JQogICAgZ3JvdXBfYnkoU2FtcGxlU2l6ZSkgJT4lCiAgICBzdW1tYXJpemUoCiAgICAgIENvdW50X0xfRWRnZV93ZWlnaHQgPSBzdW0oIWlzLm5hKExfRWRnZV93ZWlnaHQpKSwKICAgICAgQ291bnRfT19FZGdlX3dlaWdodCA9IHN1bSghaXMubmEoT19FZGdlX3dlaWdodCkpLAogICAgICBDb3VudF9Db3ZfRWRnZV93ZWlnaHQgPSBzdW0oIWlzLm5hKENvdl9FZGdlX3dlaWdodCkpCiAgICApICU+JQogICAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBzdGFydHNfd2l0aCgiQ291bnQiKSwgbmFtZXNfdG8gPSAiQ291bnRzX0NvbHVtbl9OYW1lIiwgdmFsdWVzX3RvID0gIkNvdW50IikgJT4lCiAgICBtdXRhdGUoRGF0YVR5cGUgPSBjYXNlX3doZW4oCiAgICAgIENvdW50c19Db2x1bW5fTmFtZSA9PSAiQ291bnRfTF9FZGdlX3dlaWdodCIgfiAiTElNT04iLAogICAgICBDb3VudHNfQ29sdW1uX05hbWUgPT0gIkNvdW50X09fRWRnZV93ZWlnaHQiIH4gIlRydWUiLAogICAgICBDb3VudHNfQ29sdW1uX05hbWUgPT0gIkNvdW50X0Nvdl9FZGdlX3dlaWdodCIgfiAiU1BJRUMtRUFTSSIKICAgICkpCiAgdG90YWxfZWRnZXMkVGltZSA8LSB0aW1lCiAgCiAgIyBTdG9yZSB0aGUgcmVzdWx0IGluIHRoZSBsaXN0CiAgdG90YWxfZWRnZXNfbGlzdFtbdGltZV1dIDwtIHRvdGFsX2VkZ2VzCn0KCiMgQ29tYmluZSBhbGwgdGltZXBvaW50cyB0b2dldGhlcgpUb3RhbF9FZGdlcyA8LSBkby5jYWxsKHJiaW5kLCB0b3RhbF9lZGdlc19saXN0KQoKYGBgCgoKIyMjIDMuMi4yIC0gU3RhdGlzdGljcwoKCkNoZWNrIG5vcm1hbGN5IHRvIGRldGVybWluZSBpZiB0LXRlc3Qgb3IgaWYgd2lsY294IHJhbmsgc3VtCmBgYHtyfQojIFN0ZXAgMSAtIFJ1biBTaGFwaXJvIFdpbGtzIHRvIHRlc3QgZm9yIG5vcm1hbGFjeSBvZiB0aGUgZGF0YQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzaGFwaXJvX3Jlc3VsdCA8LSBzaGFwaXJvLnRlc3QoVG90YWxfRWRnZXMkQ291bnQpCnBfdmFsdWUgPC0gc2hhcGlyb19yZXN1bHQkcC52YWx1ZQoKIyBTdGVwIDIgLSBQcmludCBwLXZhbHVlIG9uIHRoZSBoaXN0b2dyYW0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBDcmVhdGUgdGhlIGhpc3RvZ3JhbSBwbG90Cmhpc3QoVG90YWxfRWRnZXMkQ291bnQsIGJyZWFrcz0xMDAsIAogICAgIG1haW4gPSAiVG90YWwgRWRnZXMiLCAKICAgICB4bGFiID0gIkNvdW50IG9mIGVkZ2VzIiwgeWxhYiA9ICJGcmVxdWVuY3kiKQojIEFkZCB0aGUgcC12YWx1ZSBhbm5vdGF0aW9uCnRleHQoeCA9IDEwMCwgeSA9IDEwLCAKICAgICBsYWJlbHMgPSBwYXN0ZSgiU2hhcGlyby1XaWxrIHAtdmFsdWU6ICIsIHBfdmFsdWUpKQoKYGBgCgpEYXRhIGlzIG5vdCBub3JtYWxseSBkaXN0cmlidXRlZCwgdXNlIFdpbGNveCBSYW5rIHN1bSBpc250ZWFkIG9mIFQtdGVzdAoKCgpgYGB7cn0KIyBTdGVwIDE6IFN1bW1hcnkgU3RhdGlzdGljcwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpTdW1tYXJ5X2RhdGEgPC0gVG90YWxfRWRnZXMgJT4lCiAgZ3JvdXBfYnkoU2FtcGxlU2l6ZSkgJT4lCiAgc3VtbWFyaXNlKAogICAgIyBMSU1PTgogICAgbWVhbl9lZGdlc19MSU1PTiA9IG1lYW4oQ291bnRbRGF0YVR5cGUgPT0gIkxJTU9OIl0sIG5hLnJtID0gVFJVRSksCiAgICBzZF9lZGdlc19MSU1PTiA9IHNkKENvdW50W0RhdGFUeXBlID09ICJMSU1PTiJdLCBuYS5ybSA9IFRSVUUpLAogICAgbl9MSU1PTiA9IHN1bShEYXRhVHlwZSA9PSAiTElNT04iLCBuYS5ybSA9IFRSVUUpLAogICAgCiAgICAjIFNQSUVDLUVBU0kgKyBDT1YKICAgIG1lYW5fZWRnZXNfU1BJRUNFQVNJID0gbWVhbihDb3VudFtEYXRhVHlwZSA9PSAiU1BJRUMtRUFTSSJdLCBuYS5ybSA9IFRSVUUpLAogICAgc2RfZWRnZXNfU1BJRUNFQVNJID0gc2QoQ291bnRbRGF0YVR5cGUgPT0gIlNQSUVDLUVBU0kiXSwgbmEucm0gPSBUUlVFKSwKICAgIG5fU1BJRUNFQVNJID0gc3VtKERhdGFUeXBlID09ICJTUElFQy1FQVNJIiwgbmEucm0gPSBUUlVFKSwKICAgIAogICAgIyBTUElFQy1FQVNJIG5vIENPVgogICAgbWVhbl9lZGdlc19UUlVFID0gbWVhbihDb3VudFtEYXRhVHlwZSA9PSAiVHJ1ZSJdLCBuYS5ybSA9IFRSVUUpLAogICAgc2RfZWRnZXNfVFJVRSA9IHNkKENvdW50W0RhdGFUeXBlID09ICJUcnVlIl0sIG5hLnJtID0gVFJVRSksCiAgICBuX1RSVUUgPSBzdW0oRGF0YVR5cGUgPT0gIlRydWUiLCBuYS5ybSA9IFRSVUUpLAogICAgCiAgICAjIHAtdmFsdWVzCiAgICBwX3ZhbHVlX0xJTU9OX1RSVUUgPSBpZiAobl9MSU1PTiA+IDAgJiBuX1RSVUUgPiAwKSAKICAgICAgdGlkeShleGFjdFJhbmtUZXN0czo6d2lsY294LmV4YWN0KENvdW50W0RhdGFUeXBlICVpbiUgYygiTElNT04iLCAiVHJ1ZSIpXSB+IAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBEYXRhVHlwZVtEYXRhVHlwZSAlaW4lIGMoIkxJTU9OIiwgIlRydWUiKV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXhhY3QgPSBGQUxTRSkpJHAudmFsdWUgZWxzZSBOQSwKICAgIHBfdmFsdWVfTElNT05fU1BJRUNFQVNJID0gaWYgKG5fTElNT04gPiAwICYgbl9TUElFQ0VBU0kgPiAwKSAKICAgICAgdGlkeShleGFjdFJhbmtUZXN0czo6d2lsY294LmV4YWN0KENvdW50W0RhdGFUeXBlICVpbiUgYygiTElNT04iLCAiU1BJRUMtRUFTSSIpXSB+IAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBEYXRhVHlwZVtEYXRhVHlwZSAlaW4lIGMoIkxJTU9OIiwgIlNQSUVDLUVBU0kiKV0sIAogICAgICAgICAgICAgICAgICAgICAgIGV4YWN0ID0gRkFMU0UpKSRwLnZhbHVlIGVsc2UgTkEsCiAgICAgICAgICAgICAgICAgICAgICAgcF92YWx1ZV9TUElFQ0VBU0lfVFJVRSA9IGlmIChuX1NQSUVDRUFTSSA+IDAgJiBuX1RSVUUgPiAwKSAKICAgICAgdGlkeShleGFjdFJhbmtUZXN0czo6d2lsY294LmV4YWN0KENvdW50W0RhdGFUeXBlICVpbiUgYygiU1BJRUMtRUFTSSIsICJUcnVlIildIH4gCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIERhdGFUeXBlW0RhdGFUeXBlICVpbiUgYygiU1BJRUMtRUFTSSIsICJUcnVlIildLCAKICAgICAgICAgICAgICAgICAgICAgICBleGFjdCA9IEZBTFNFKSkkcC52YWx1ZSBlbHNlIE5BCiAgKSAlPiUKICBtdXRhdGUoCiAgICBzZV9lZGdlc19MSU1PTiA9IHNkX2VkZ2VzX0xJTU9OIC8gc3FydChuX0xJTU9OKSwKICAgIHNlX2VkZ2VzX1NQSUVDRUFTSSA9IHNkX2VkZ2VzX1NQSUVDRUFTSSAvIHNxcnQobl9TUElFQ0VBU0kpLAogICAgc2VfZWRnZXNfVFJVRSA9IHNkX2VkZ2VzX1RSVUUgLyBzcXJ0KG5fVFJVRSksCiAgICBzaWduaWZpY2FuY2VfTElNT05fVFJVRSA9IHNhcHBseShwX3ZhbHVlX0xJTU9OX1RSVUUsIHBfdmFsdWVfc2lnKSwKICAgIHNpZ25pZmljYW5jZV9MSU1PTl9TUElFQ0VBU0kgPSBzYXBwbHkocF92YWx1ZV9MSU1PTl9TUElFQ0VBU0ksIHBfdmFsdWVfc2lnKSwKICAgIHNpZ25pZmljYW5jZV9TUElFQ0VBU0lfVFJVRSA9IHNhcHBseShwX3ZhbHVlX1NQSUVDRUFTSV9UUlVFLCBwX3ZhbHVlX3NpZykKICApCgojIFN0ZXAgMjogTWFrZSB0aGUgcGxvdHRpbmcgZGF0YQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpTdW1tYXJ5X2RhdGFfbG9uZyA8LSBTdW1tYXJ5X2RhdGEgJT4lCiAgZHBseXI6OnNlbGVjdChTYW1wbGVTaXplLCBzdGFydHNfd2l0aCgibWVhbl9lZGdlcyIpLCBzdGFydHNfd2l0aCgic2VfZWRnZXMiKSwgc3RhcnRzX3dpdGgoInNpZ25pZmljYW5jZSIpKSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IHN0YXJ0c193aXRoKCJtZWFuX2VkZ2VzIiksIG5hbWVzX3RvID0gIkRhdGFUeXBlIiwgdmFsdWVzX3RvID0gIm1lYW5fZWRnZXMiKSAlPiUKICBtdXRhdGUoRGF0YVR5cGUgPSBjYXNlX3doZW4oCiAgICBzdHJfZGV0ZWN0KERhdGFUeXBlLCAiTElNT04iKSB+ICJMSU1PTiIsCiAgICBzdHJfZGV0ZWN0KERhdGFUeXBlLCAiU1BJRUNFQVNJIikgfiAiU1BJRUMtRUFTSSIsCiAgICBzdHJfZGV0ZWN0KERhdGFUeXBlLCAiVFJVRSIpIH4gIlRydWUiCiAgKSkgJT4lCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBzdGFydHNfd2l0aCgic2VfZWRnZXMiKSwgbmFtZXNfdG8gPSAiRGF0YVR5cGVfc2UiLCB2YWx1ZXNfdG8gPSAic2VfZWRnZXMiKSAlPiUKICBtdXRhdGUoRGF0YVR5cGVfc2UgPSBjYXNlX3doZW4oCiAgICBzdHJfZGV0ZWN0KERhdGFUeXBlX3NlLCAiTElNT04iKSB+ICJMSU1PTiIsCiAgICBzdHJfZGV0ZWN0KERhdGFUeXBlX3NlLCAiU1BJRUNFQVNJIikgfiAiU1BJRUMtRUFTSSIsCiAgICBzdHJfZGV0ZWN0KERhdGFUeXBlX3NlLCAiVFJVRSIpIH4gIlRydWUiCiAgKSkgJT4lCiAgZmlsdGVyKERhdGFUeXBlID09IERhdGFUeXBlX3NlKSAlPiUKICBkcGx5cjo6c2VsZWN0KC1EYXRhVHlwZV9zZSkKCiMgQWRkIHNpZ25pZmljYW5jZSBsYWJlbHMKU3VtbWFyeV9kYXRhX2xvbmcgPC0gU3VtbWFyeV9kYXRhX2xvbmcgJT4lCiAgbGVmdF9qb2luKAogICAgU3VtbWFyeV9kYXRhICU+JSBkcGx5cjo6c2VsZWN0KFNhbXBsZVNpemUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpZ25pZmljYW5jZV9MSU1PTl9UUlVFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaWduaWZpY2FuY2VfTElNT05fU1BJRUNFQVNJLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaWduaWZpY2FuY2VfU1BJRUNFQVNJX1RSVUUpLAogICAgYnkgPSAiU2FtcGxlU2l6ZSIKICApCmBgYAoKCgoKYGBge3J9CiMgU3RlcCAzOiBGaW5hbCBmaWd1cmUKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKZ2dwbG90KFN1bW1hcnlfZGF0YV9sb25nLCBhZXMoeCA9IFNhbXBsZVNpemUsIHkgPSBtZWFuX2VkZ2VzLCBjb2xvciA9IERhdGFUeXBlKSkgKwogIGdlb21fbGluZSgpICsKICBnZW9tX3BvaW50KCkgKwogIAogICMgQWRkIEVycm9yIGJhcnMKICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluID0gbWVhbl9lZGdlcyAtIHNlX2VkZ2VzLCAKICAgICAgICAgICAgICAgICAgICB5bWF4ID0gbWVhbl9lZGdlcyArIHNlX2VkZ2VzKSwgd2lkdGggPSAxLjApICsKICAKICAgICMgQWRkIGEgbGVnZW5kIHRvIGRlc2NyaWJlIGFsbCB0aGUgY29sb3JzCiAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWUgPSAiTmV0d29yayIsIAogICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBjKCJMSU1PTiIgPSAib3JhbmdlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNQSUVDLUVBU0kiID0gImJsdWUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVHJ1ZSIgPSAiZ3JleSIpLAogICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJMSU1PTiIgPSAiTElNT04iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU1BJRUMtRUFTSSIgPSAiU1BJRUMtRUFTSSArIENvdmFyaWF0ZXMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVHJ1ZSIgPSAiU1BJRUMtRUFTSSAtIE5vIENvdmFyaWF0ZXMiKSkgKwogIAogICMgVXNlIGdnbmV3c2NhbGUgdG8gZGVmaW5lIGEgbmV3IGNvbG9yIHNjYWxlIGFmdGVyIHRoZSBmaXJzdCBvbmUKICBnZ25ld3NjYWxlOjpuZXdfc2NhbGVfY29sb3IoKSArCiAgCiAgIyBBbm5vdGF0ZSBMSU1PTiB2cyBUUlVFIHNpZ25pZmljYW5jZSBsZXZlbAogIGdlb21fdGV4dChhZXMobGFiZWwgPSBzaWduaWZpY2FuY2VfTElNT05fVFJVRS54LCB5ID0gKG1lYW5fZWRnZXMgKyBzZV9lZGdlcyArIDUpLCAKICAgICAgICAgICAgICAgIGNvbG9yID0gZmFjdG9yKCJMSU1PTi1UcnVlIikpLCAKICAgICAgICAgICAgc2l6ZSA9IDUsIHZqdXN0ID0gLTAuNSwgY2hlY2tfb3ZlcmxhcCA9IFRSVUUsIAogICAgICAgICAgICBkYXRhID0gU3VtbWFyeV9kYXRhX2xvbmcgJT4lIGZpbHRlcihEYXRhVHlwZSA9PSAiTElNT04iKSkgKyAKICAKICAjIEFubm90YXRlIExJTU9OIHZzIFNQSUVDLUVBU0kgc2lnbmlmaWNhbmNlIGxldmVsCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHNpZ25pZmljYW5jZV9MSU1PTl9TUElFQ0VBU0kueCwgeSA9IChtZWFuX2VkZ2VzICsgc2VfZWRnZXMgKyAxNSksIAogICAgICAgICAgICAgICAgY29sb3IgPSBmYWN0b3IoIkxJTU9OLVNQSUVDRUFTSSIpKSwgCiAgICAgICAgICAgIHNpemUgPSA1LCB2anVzdCA9IC0wLjUsIGNoZWNrX292ZXJsYXAgPSBUUlVFLCAKICAgICAgICAgICAgZGF0YSA9IFN1bW1hcnlfZGF0YV9sb25nICU+JSBmaWx0ZXIoRGF0YVR5cGUgPT0gIkxJTU9OIikpICsgCiAgCiAgIyBBbm5vdGF0ZSBTUElFQy1FQVNJIHZzIFRSVUUgc2lnbmlmaWNhbmNlIGxldmVsCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHNpZ25pZmljYW5jZV9TUElFQ0VBU0lfVFJVRS54LCB5ID0gKG1lYW5fZWRnZXMgKyBzZV9lZGdlcyArIDgpLCAKICAgICAgICAgICAgICAgIGNvbG9yID0gZmFjdG9yKCJTUElFQ0VBU0ktVHJ1ZSIpKSwgCiAgICAgICAgICAgIHNpemUgPSA1LCB2anVzdCA9IC0wLjUsIGNoZWNrX292ZXJsYXAgPSBUUlVFLCAKICAgICAgICAgICAgZGF0YSA9IFN1bW1hcnlfZGF0YV9sb25nICU+JSBmaWx0ZXIoRGF0YVR5cGUgPT0gIlNQSUVDLUVBU0kiKSkgKyAKICAKICAjIEFkZCBvbiBsYWJlbHMgYW5kIHRoZW1lcwogIGxhYnMoeCA9ICJTYW1wbGUgU2l6ZSIsIHkgPSAiVG90YWwgTmV0d29yayBFZGdlcyIpICsKICB5bGltKDAsMTUwKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoMTAsIDEwMCksIGJyZWFrcyA9IGMoMTAsMjAsNTAsNzUsMTAwKSkgICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJhcmlhbCIsY29sb3IgPSAiYmxhY2siLCBzaXplID0gMTgpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJhcmlhbCIsY29sb3IgPSAiYmxhY2siLCBzaXplID0gMTgpLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiYXJpYWwiLGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDE0KSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gImFyaWFsIixjb2xvciA9ICJibGFjayIsIHNpemUgPSAxNCksCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwgICAjIEluY3JlYXNlcyBsZWdlbmQgdGV4dCBzaXplCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMikpICsKICAKICAjIEFkZCBhIGxlZ2VuZCB0byBkZXNjcmliZSBhbGwgdGhlIGNvbG9ycwogIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lID0gIldpbGNveG9uIHNpZ25maWNhbmNlIiwgCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoIkxJTU9OLVRydWUiID0gImRhcmtvcmFuZ2UyIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkxJTU9OLVNQSUVDRUFTSSIgPSAiYmxhY2siLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTUElFQ0VBU0ktVHJ1ZSIgPSAiZGVlcHNreWJsdWUiKSwKICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiTElNT04tVHJ1ZSIgPSAiTElNT04qTm8gQ292IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkxJTU9OLVNQSUVDRUFTSSIgPSAiTElNT04qU1BJRUNFQVNJIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU1BJRUNFQVNJLVRydWUiID0gIlNQSUVDRUFTSSpObyBDb3YiKSkKYGBgCgoKCgojIyAzLjMgIFN0cmVuZ3RoIG9mIFJlY292ZXJlZCBFZGdlcwoKCiMjIyAzLjMuMSAtIERhdGEgRm9ybWF0dGluZwpSZWFkIGJhY2sgaW4gdGhlIHJhdyBkYXRhIHdpdGggbm8gY292YXJpYXRlcywgZ2V0IHRoZSBzcGVhcm1hbiBjb3JyZWxhdGlvbiBwZXIgdGltZSBwb2ludCAoMSwyLDMsNCw1KSBhbmQgcGVyIHNhbXBsZSBzaXplIGxldmVsICgxMCwyMCw1MCw3NSwxMDApLiBXZSB3aWxsIHRoZW4gY29tcGFyZSB0aGVzZSBzdHJlbmd0aCBvZiByZWNvdmVyZWQgZWRnZXMgdG8gdGhlIGVkZ2VzIHJlY292ZXJlZCBieSBvdXIgdmFyaW91cyB0ZXN0cyBhYm92ZS4gCmBgYHtyfQpSYXcxMCA8LSByZWFkLmNzdihoZXJlKCJEYXRhIiwgIkdMVl9TaW1EYXRhIiwgIkRhdGFzZXRfMiIsIkdMVl9OMTAuY3N2IikpICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sdW1uX3RvX3Jvd25hbWVzKCJYIikgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkcGx5cjo6c2VsZWN0KC1jKFRpbWUsIElELCBTZXgsIEJNSSwgQWdlKSkKUmF3MjAgPC0gcmVhZC5jc3YoaGVyZSgiRGF0YSIsICJHTFZfU2ltRGF0YSIsICJEYXRhc2V0XzIiLCJHTFZfTjIwLmNzdiIpKSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbl90b19yb3duYW1lcygiWCIpICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZHBseXI6OnNlbGVjdCgtYyhUaW1lLCBJRCwgU2V4LCBCTUksIEFnZSkpClJhdzUwIDwtIHJlYWQuY3N2KGhlcmUoIkRhdGEiLCAiR0xWX1NpbURhdGEiLCAiRGF0YXNldF8yIiwiR0xWX041MC5jc3YiKSkgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fdG9fcm93bmFtZXMoIlgiKSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRwbHlyOjpzZWxlY3QoLWMoVGltZSwgSUQsIFNleCwgQk1JLCBBZ2UpKQpSYXc3NSA8LSByZWFkLmNzdihoZXJlKCJEYXRhIiwgIkdMVl9TaW1EYXRhIiwgIkRhdGFzZXRfMiIsIkdMVl9ONzUuY3N2IikpICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sdW1uX3RvX3Jvd25hbWVzKCJYIikgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkcGx5cjo6c2VsZWN0KC1jKFRpbWUsIElELCBTZXgsIEJNSSwgQWdlKSkKUmF3MTAwIDwtIHJlYWQuY3N2KGhlcmUoIkRhdGEiLCAiR0xWX1NpbURhdGEiLCAiRGF0YXNldF8yIiwiR0xWX04xMDAuY3N2IikpICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sdW1uX3RvX3Jvd25hbWVzKCJYIikgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkcGx5cjo6c2VsZWN0KC1jKFRpbWUsIElELCBTZXgsIEJNSSwgQWdlKSkKYGBgCgoKCmBgYHtyfQojIERlZmluZSBzYW1wbGUgc2l6ZXMgYW5kIGNvcnJlc3BvbmRpbmcgZGF0YSBmcmFtZXMKc2FtcGxlX3NpemVzIDwtIGMoMTAsIDIwLCA1MCwgNzUsIDEwMCkKZGF0YV9mcmFtZXMgPC0gbGlzdChSYXcxMCwgUmF3MjAsIFJhdzUwLCBSYXc3NSwgUmF3MTAwKQoKIyBJbml0aWFsaXplIGFuIGVtcHR5IGxpc3QgdG8gc3RvcmUgcmVzdWx0cyBmb3IgZWFjaCBzYW1wbGUgc2l6ZQpjb3ZfcmVzdWx0cyA8LSBsaXN0KCkKCmZvciAoaSBpbiBzZXFfYWxvbmcoc2FtcGxlX3NpemVzKSkgewogIGRmIDwtIGRhdGFfZnJhbWVzW1tpXV0KICBzYW1wbGVfc2l6ZSA8LSBzYW1wbGVfc2l6ZXNbaV0KICAKICBkZiRTdWJqZWN0IDwtIHN1YigiX1RpbWVbMC05XSsiLCAiIiwgcm93bmFtZXMoZGYpKQogIGRmJFRpbWVwb2ludCA8LSBzdWIoIlNialswLTldK18iLCAiIiwgcm93bmFtZXMoZGYpKQogIGRmJFRpbWVwb2ludCA8LSBnc3ViKCJUaW1lIiwgIiIsIGRmJFRpbWVwb2ludCkKICAKICAjIExpc3Qgb2YgdW5pcXVlIHRpbWVwb2ludHMKICB0aW1lcG9pbnRzIDwtIHVuaXF1ZShkZiRUaW1lcG9pbnQpCiAgCiAgIyBJbml0aWFsaXplIGFuIGVtcHR5IGRhdGEgZnJhbWUgdG8gc3RvcmUgcmVzdWx0cwogIHJlc3VsdF9kZiA8LSBkYXRhLmZyYW1lKFNvdXJjZSA9IGNoYXJhY3RlcigpLAogICAgICAgICAgICAgICAgICAgICAgICAgIFNpbmsgPSBjaGFyYWN0ZXIoKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBDb3ZhcmlhbmNlID0gbnVtZXJpYygpLAogICAgICAgICAgICAgICAgICAgICAgICAgIFRpbWVwb2ludCA9IGNoYXJhY3RlcigpLAogICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKICAKICAjIExvb3AgdGhyb3VnaCBlYWNoIHRpbWVwb2ludAogIGZvciAodGltZSBpbiB0aW1lcG9pbnRzKSB7CiAgICAjIFN1YnNldCBkYXRhIGZvciB0aGUgY3VycmVudCB0aW1lcG9pbnQKICAgIGRmX3RpbWUgPC0gZGYgJT4lIGZpbHRlcihUaW1lcG9pbnQgPT0gdGltZSkgJT4lIGRwbHlyOjpzZWxlY3QoLVN1YmplY3QsIC1UaW1lcG9pbnQpCiAgICAKICAgICMgQ2FsY3VsYXRlIGNvdmFyaWFuY2UgbWF0cml4IGZvciB0aGUgY3VycmVudCB0aW1lcG9pbnQKICAgIGNvdl9tYXRyaXggPC0gY29yKGRmX3RpbWUsIG1ldGhvZCA9ICJzcGVhcm1hbiIpCiAgICAKICAgICMgQ29udmVydCB0aGUgY292YXJpYW5jZSBtYXRyaXggdG8gYSBsb25nIGZvcm1hdAogICAgY292X2xvbmcgPC0gYXMuZGF0YS5mcmFtZShhcy50YWJsZShjb3ZfbWF0cml4KSkKICAgIAogICAgIyBkcGx5cjo6cmVuYW1lIGNvbHVtbnMgZm9yIGNsYXJpdHkKICAgIG5hbWVzKGNvdl9sb25nKSA8LSBjKCJTaW5rIiwgIlNvdXJjZSIsICAiQ292YXJpYW5jZSIpCiAgICAKICAgICMgQWRkIHRpbWVwb2ludCBpbmZvcm1hdGlvbgogICAgY292X2xvbmckVGltZXBvaW50IDwtIHRpbWUKICAgIAogICAgIyBDb21iaW5lIHdpdGggdGhlIHJlc3VsdCBkYXRhIGZyYW1lCiAgICByZXN1bHRfZGYgPC0gYmluZF9yb3dzKHJlc3VsdF9kZiwgY292X2xvbmcpCiAgfQogIAogICMgQWRkIHNhbXBsZSBzaXplIGluZm9ybWF0aW9uCiAgcmVzdWx0X2RmIDwtIHJlc3VsdF9kZiAlPiUgZHBseXI6OnJlbmFtZSgiVGltZSIgPSJUaW1lcG9pbnQiKQogIHJlc3VsdF9kZiRTYW1wbGVTaXplIDwtIHNhbXBsZV9zaXplCiAgCiAgIyBTdG9yZSBpbiB0aGUgbGlzdAogIGNvdl9yZXN1bHRzW1tpXV0gPC0gcmVzdWx0X2RmCn0KCiMgTWVyZ2UgYWxsIHJlc3VsdHMgaW50byBvbmUgZGF0YSBmcmFtZQpUcnVlX2NvdiA8LSBkby5jYWxsKHJiaW5kLCBjb3ZfcmVzdWx0cykKYGBgCgoKTWVyZ2UgdG9nZXRoZXIgYW5kIGFkZCBhbm5vdGFpdG9uIGZvciBzdHJlbmd0aCBvZiBlZGdlcyBkZXRlY3RlZCAgCi0gdGhlIHggYXhpcyB3aWxsIGJlIGEgcmFuZ2UgZnJvbSB0aGUgIkNvdmFyaWFuY2UiIGNvbHVtbiB3aXRoIGVkZ2VzIGJldHdlZW4gIC0xIC0gLTAuNSwgLTAuNSAtIC0wLjEsIC0wLjEgLSAwLjEsIDAuMSAtMC41LCAwLjUgLSAxLiAgCi0gdGhlIHkgYXhpcyB3aWxsIGJlIHdoYXQgcGVyY2VudGFnZSBvZiB0aGUgZWRnZXMgZGV0ZWN0ZWQgZm9yIHRoYXQgbmV0d29yay9tZXRob2QgZmFsbCBpbnRvIGVhY2ggb2YgdGhlc2UgY2F0ZWdvcmllcwpgYGB7cn0KIyBNZXJnZSB3aXRoIG15IGRhdGEKU3RyZW5ndGhfZGF0YSA8LSBtZXJnZShUcnVlX2NvdiwgU3ViamVjdF9zZW5zX2RhdGFfdGF4YSwgYnkueSA9IGMoIlNvdXJjZSIsICJTaW5rIiwgIlRpbWUiLCAiU2FtcGxlU2l6ZSIpLCBhbGwgPSBUUlVFKQoKIyBBZGQgaW4gY29sdW1ucyBiYXNlZCBvbiB3aGF0IHN0cmVuZ3RoIG9mIGFzc29jaWF0aW9uIGl0IGRldGVjdGVkClN0cmVuZ3RoX2RhdGEgPC0gU3RyZW5ndGhfZGF0YSAlPiUgCiAgIyBmb3IgTElNT04gcmVjb3ZlcnkKICBtdXRhdGUoTElNT05fc3RyZW5ndGggPSBjYXNlX3doZW4oCiAgICAhaXMubmEoTF9FZGdlX3dlaWdodCkgJiBDb3ZhcmlhbmNlID49IC0xICYgQ292YXJpYW5jZSA8IC0wLjUgfiAiLTEgLSAtMC41IiwKICAgICFpcy5uYShMX0VkZ2Vfd2VpZ2h0KSAmIENvdmFyaWFuY2UgPj0gLTAuNSAmIENvdmFyaWFuY2UgPCAtMC4xIH4gIi0wLjUgLSAtMC4xIiwKICAgICFpcy5uYShMX0VkZ2Vfd2VpZ2h0KSAmIENvdmFyaWFuY2UgPj0gLTAuMSAmIENvdmFyaWFuY2UgPCAwLjEgfiAiLTAuMSAtIDAuMSIsCiAgICAhaXMubmEoTF9FZGdlX3dlaWdodCkgJiBDb3ZhcmlhbmNlID49IDAuMSAmIENvdmFyaWFuY2UgPCAwLjUgfiAiMC4xIC0gMC41IiwKICAgICFpcy5uYShMX0VkZ2Vfd2VpZ2h0KSAmIENvdmFyaWFuY2UgPj0gMC41ICYgQ292YXJpYW5jZSA8PSAxIH4gIjAuNSAtIDEiLAogICAgVFJVRSB+IGFzLmNoYXJhY3RlcihOQSkgIAogICkpICU+JSAKICAjIGZvciBTUElFQy1FQVNJIHJlY292ZXJ5CiAgbXV0YXRlKFNQSUVDRUFTSV9zdHJlbmd0aCA9IGNhc2Vfd2hlbigKICAgICFpcy5uYShDb3ZfRWRnZV93ZWlnaHQpICYgQ292YXJpYW5jZSA+PSAtMSAmIENvdmFyaWFuY2UgPCAtMC41IH4gIi0xIC0gLTAuNSIsCiAgICAhaXMubmEoQ292X0VkZ2Vfd2VpZ2h0KSAmIENvdmFyaWFuY2UgPj0gLTAuNSAmIENvdmFyaWFuY2UgPCAtMC4xIH4gIi0wLjUgLSAtMC4xIiwKICAgICFpcy5uYShDb3ZfRWRnZV93ZWlnaHQpICYgQ292YXJpYW5jZSA+PSAtMC4xICYgQ292YXJpYW5jZSA8IDAuMSB+ICItMC4xIC0gMC4xIiwKICAgICFpcy5uYShDb3ZfRWRnZV93ZWlnaHQpICYgQ292YXJpYW5jZSA+PSAwLjEgJiBDb3ZhcmlhbmNlIDwgMC41IH4gIjAuMSAtIDAuNSIsCiAgICAhaXMubmEoQ292X0VkZ2Vfd2VpZ2h0KSAmIENvdmFyaWFuY2UgPj0gMC41ICYgQ292YXJpYW5jZSA8PSAxIH4gIjAuNSAtIDEiLAogICAgVFJVRSB+IGFzLmNoYXJhY3RlcihOQSkgIAogICkpCgojIFNwbGl0IGludG8gVHJ1ZSBQb3NpdGl2ZXMgRGF0YXNldCBhbmQgRmFsc2UgUG9zaXRpdmVzIERhdGFzZXRzClRydWVfcG9zX3JhdyA8LSBTdHJlbmd0aF9kYXRhICU+JSBmaWx0ZXIoIWlzLm5hKE9fRWRnZV93ZWlnaHQpKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcighKGlzLm5hKExfRWRnZV93ZWlnaHQpICYgaXMubmEoQ292X0VkZ2Vfd2VpZ2h0KSkpCkZhbHNlX3Bvc19yYXcgPC0gU3RyZW5ndGhfZGF0YSAlPiUgZmlsdGVyKGlzLm5hKE9fRWRnZV93ZWlnaHQpKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcighKGlzLm5hKExfRWRnZV93ZWlnaHQpICYgaXMubmEoQ292X0VkZ2Vfd2VpZ2h0KSkpCgpgYGAKCgojIyMgMy4zLjIgLSBTdGF0aXN0aWNzCgoKVHJ1ZSBQb3NpdGl2ZXMKYGBge3J9CiMgU3RlcCAxIHR1cm4gdG8gbG9uZyBmb3JtYXQKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCmxvbmdfZGF0YSA8LSBUcnVlX3Bvc19yYXcgJT4lCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKCJMSU1PTl9zdHJlbmd0aCIsICJTUElFQ0VBU0lfc3RyZW5ndGgiKSwKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiTmV0d29yayIsCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJXZWlnaHQiLAogICAgICAgICAgICAgICB2YWx1ZXNfZHJvcF9uYSA9IFRSVUUpICU+JQogIG11dGF0ZShOZXR3b3JrID0gY2FzZV93aGVuKAogICAgTmV0d29yayA9PSAiTElNT05fc3RyZW5ndGgiIH4gIkxJTU9OIiwgCiAgICBOZXR3b3JrID09ICJTUElFQ0VBU0lfc3RyZW5ndGgiIH4gIlNQSUVDRUFTSSIpKSAKCgojIFN0ZXAgMjogRmluZCB0b3RhbCBuZXR3b3JrcyBwZXIgTmV0d29yayB0eXBlIGFuZCBzYW1wbGUgc2l6ZQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdW1tYXJ5X2RhdGEgPC0gbG9uZ19kYXRhICU+JQogIGdyb3VwX2J5KE5ldHdvcmssIFNhbXBsZVNpemUpICU+JQogIG11dGF0ZShUb3RhbF9lZGdlID0gc3VtKCFpcy5uYShXZWlnaHQpKSkKCiMgU3RlcCAzOiBUdXJuIHRvIHBlcmNlbnRhZ2VzIGF2ZXJhZ2luZyBieSB0aW1lIHRvIHBsb3QKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKc3VtbWFyeV9kYXRhMiA8LSBzdW1tYXJ5X2RhdGEgJT4lCiAgZ3JvdXBfYnkoTmV0d29yaywgU2FtcGxlU2l6ZSkgJT4lCiAgbXV0YXRlKHRvdGFsX2NvdW50ID0gbigpKSAlPiUKICBncm91cF9ieShOZXR3b3JrLCBTYW1wbGVTaXplLCBXZWlnaHQpICU+JQogIHN1bW1hcml6ZShjb3VudCA9IG4oKSwKICAgICAgICAgICAgbWVhbl9wZXJjZW50YWdlID0gKGNvdW50IC8gZHBseXI6OmZpcnN0KHRvdGFsX2NvdW50KSksIAogICAgICAgICAgICAuZ3JvdXBzID0gJ2Ryb3AnKQoKCiMgU3RlcCA0OiBQbG90CiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU3RhY2tlZCArIHBlcmNlbnQKZ2dwbG90KHN1bW1hcnlfZGF0YTIsIGFlcyhmaWxsPVdlaWdodCwgeT1tZWFuX3BlcmNlbnRhZ2UsIHg9YXMuZmFjdG9yKFNhbXBsZVNpemUpKSkgKyAKICBnZW9tX2Jhcihwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC44KSwgc3RhdCA9ICJpZGVudGl0eSIsIHdpZHRoID0gMC43KSArIAogIGZhY2V0X3dyYXAofiBOZXR3b3JrKSArIAogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWUgPSAiRWRnZSBXZWlnaHQiLAogICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoIi0xIC0gLTAuNSIgPSAiZGFya3JlZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiLTAuNSAtIC0wLjEiID0gImluZGlhbnJlZDEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIi0wLjEgLSAwLjEiID0gIm1vY2Nhc2luIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIwLjEgLSAwLjUiID0gImxpZ2h0Ymx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMC41IC0gMSIgPSAiZGFya2JsdWUiKSwKICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKCItMSAtIC0wLjUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIi0wLjUgLSAtMC4xIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICItMC4xIC0gMC4xIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIwLjEgLSAwLjUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjAuNSAtIDEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk5BIikpICsKICAjIEFkZCBvbiBsYWJlbHMgYW5kIHRoZW1lcwogIGxhYnMoeCA9ICJTYW1wbGUgU2l6ZSIsIHkgPSAiUGVyY2VudCBUcnVlIFBvc2l0aXZlcyIpICsKICB5bGltKDAsMSkgKwogIHNjYWxlX3hfZGlzY3JldGUoKSArCiAgdGhlbWVfbGluZWRyYXcoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gImFyaWFsIixjb2xvciA9ICJibGFjayIsIHNpemUgPSAxNCksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gImFyaWFsIixjb2xvciA9ICJibGFjayIsIHNpemUgPSAxNCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJhcmlhbCIsY29sb3IgPSAiYmxhY2siLCBzaXplID0gMTQpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiYXJpYWwiLGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDE0KSwKICAgICAgICBzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIGZhbWlseSA9ICJhcmlhbCIsIGNvbG9yID0gIndoaXRlIiwgc2l6ZSA9IDEyKSkKYGBgCgoKCgpgYGB7cn0KZ2dwbG90KHN1bW1hcnlfZGF0YTIsIGFlcyh4ID0gU2FtcGxlU2l6ZSwgeSA9IG1lYW5fcGVyY2VudGFnZSwgZmlsbCA9IFdlaWdodCkpICsgCiAgZ2VvbV9kZW5zaXR5KHBvc2l0aW9uID0gImZpbGwiLCBzdGF0ID0gImlkZW50aXR5IiwgYWxwaGEgPSAwLjYpICsKICBmYWNldF93cmFwKH4gTmV0d29yaykgKyAKICBzY2FsZV9maWxsX21hbnVhbChuYW1lID0gIkVkZ2UgV2VpZ2h0IiwKICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBjKCItMSAtIC0wLjUiID0gImRhcmtyZWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIi0wLjUgLSAtMC4xIiA9ICJpbmRpYW5yZWQxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICItMC4xIC0gMC4xIiA9ICJtb2NjYXNpbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMC4xIC0gMC41IiA9ICJsaWdodGJsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjAuNSAtIDEiID0gImRhcmtibHVlIiksCiAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygiLTEgLSAtMC41IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICItMC41IC0gLTAuMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiLTAuMSAtIDAuMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMC4xIC0gMC41IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIwLjUgLSAxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJOQSIpKSArCiAgIyBBZGQgb24gbGFiZWxzIGFuZCB0aGVtZXMKICBsYWJzKHggPSAiU2FtcGxlIFNpemUiLCB5ID0gIkRlbnNpdHkgKFByb3BvcnRpb24gb2YgVHJ1ZSBQb3NpdGl2ZXMpIikgKwogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDExMCksIGJyZWFrcyA9IGMoMTAsMjAsNTAsNzUsMTAwKSkgICsKICB0aGVtZV9saW5lZHJhdygpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiYXJpYWwiLGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDEyKSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiYXJpYWwiLGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDEyKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gImFyaWFsIixjb2xvciA9ICJibGFjayIsIHNpemUgPSAxNCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJhcmlhbCIsY29sb3IgPSAiYmxhY2siLCBzaXplID0gMTQpLAogICAgICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgZmFtaWx5ID0gImFyaWFsIiwgY29sb3IgPSAid2hpdGUiLCBzaXplID0gMTIpKQoKYGBgCgoKCkZhbHNlIFBvc2l0aXZlcwpgYGB7cn0KIyBTdGVwIDEgdHVybiB0byBsb25nIGZvcm1hdAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKbG9uZ19kYXRhIDwtIEZhbHNlX3Bvc19yYXcgJT4lCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKCJMSU1PTl9zdHJlbmd0aCIsICJTUElFQ0VBU0lfc3RyZW5ndGgiKSwKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiTmV0d29yayIsCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJXZWlnaHQiLAogICAgICAgICAgICAgICB2YWx1ZXNfZHJvcF9uYSA9IFRSVUUpICU+JQogIG11dGF0ZShOZXR3b3JrID0gY2FzZV93aGVuKAogICAgTmV0d29yayA9PSAiTElNT05fc3RyZW5ndGgiIH4gIkxJTU9OIiwgCiAgICBOZXR3b3JrID09ICJTUElFQ0VBU0lfc3RyZW5ndGgiIH4gIlNQSUVDRUFTSSIpKSAKCgojIFN0ZXAgMjogRmluZCB0b3RhbCBuZXR3b3JrcyBwZXIgTmV0d29yayB0eXBlIGFuZCBzYW1wbGUgc2l6ZQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzdW1tYXJ5X2RhdGEgPC0gbG9uZ19kYXRhICU+JQogIGdyb3VwX2J5KE5ldHdvcmssIFNhbXBsZVNpemUpICU+JQogIG11dGF0ZShUb3RhbF9lZGdlID0gc3VtKCFpcy5uYShXZWlnaHQpKSkKCiMgU3RlcCAzOiBUdXJuIHRvIHBlcmNlbnRhZ2VzIGF2ZXJhZ2luZyBieSB0aW1lIHRvIHBsb3QKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKc3VtbWFyeV9kYXRhMiA8LSBzdW1tYXJ5X2RhdGEgJT4lCiAgZ3JvdXBfYnkoTmV0d29yaywgU2FtcGxlU2l6ZSkgJT4lCiAgbXV0YXRlKHRvdGFsX2NvdW50ID0gbigpKSAlPiUKICBncm91cF9ieShOZXR3b3JrLCBTYW1wbGVTaXplLCBXZWlnaHQpICU+JQogIHN1bW1hcml6ZShjb3VudCA9IG4oKSwKICAgICAgICAgICAgbWVhbl9wZXJjZW50YWdlID0gKGNvdW50IC8gZHBseXI6OmZpcnN0KHRvdGFsX2NvdW50KSksIAogICAgICAgICAgICAuZ3JvdXBzID0gJ2Ryb3AnKQoKCiMgU3RlcCA0OiBQbG90CiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU3RhY2tlZCArIHBlcmNlbnQKZ2dwbG90KHN1bW1hcnlfZGF0YTIsIGFlcyhmaWxsPVdlaWdodCwgeT1tZWFuX3BlcmNlbnRhZ2UsIHg9YXMuZmFjdG9yKFNhbXBsZVNpemUpKSkgKyAKICBnZW9tX2Jhcihwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC44KSwgc3RhdCA9ICJpZGVudGl0eSIsIHdpZHRoID0gMC43KSArIAogIGZhY2V0X3dyYXAofiBOZXR3b3JrKSArIAogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWUgPSAiRWRnZSBXZWlnaHQiLAogICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoIi0xIC0gLTAuNSIgPSAiZGFya3JlZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiLTAuNSAtIC0wLjEiID0gImluZGlhbnJlZDEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIi0wLjEgLSAwLjEiID0gIm1vY2Nhc2luIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIwLjEgLSAwLjUiID0gImxpZ2h0Ymx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMC41IC0gMSIgPSAiZGFya2JsdWUiKSwKICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKCItMSAtIC0wLjUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIi0wLjUgLSAtMC4xIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICItMC4xIC0gMC4xIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIwLjEgLSAwLjUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjAuNSAtIDEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk5BIikpICsKICAjIEFkZCBvbiBsYWJlbHMgYW5kIHRoZW1lcwogIGxhYnMoeCA9ICJTYW1wbGUgU2l6ZSIsIHkgPSAiUGVyY2VudCBGYWxzZSBQb3NpdGl2ZXMiKSArCiAgeWxpbSgwLDEpICsKICBzY2FsZV94X2Rpc2NyZXRlKCkgKwogIHRoZW1lX2xpbmVkcmF3KCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJhcmlhbCIsY29sb3IgPSAiYmxhY2siLCBzaXplID0gMTQpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJhcmlhbCIsY29sb3IgPSAiYmxhY2siLCBzaXplID0gMTQpLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiYXJpYWwiLGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDE0KSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gImFyaWFsIixjb2xvciA9ICJibGFjayIsIHNpemUgPSAxNCksCiAgICAgICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBmYW1pbHkgPSAiYXJpYWwiLCBjb2xvciA9ICJ3aGl0ZSIsIHNpemUgPSAxMikpCmBgYAoKCgoKIyA0IC0gTm90aWZpY2F0aW9ucwpgYGB7cn0KI3N5c3RlbSgic2F5IFlvdXIgU2lsbHkgY29kZSBmaW5pc2hlZCEiKQpgYGAKCgoKCgoKCgoKCgoKCgo=